package atom

import (
	"encoding/xml"
	"fmt"
)

type Source struct {
	XMLName xml.Name `xml:"source"`
	*CommonAttributes
	Authors      []*Person           `xml:"author,omitempty"`
	Categories   []*Category         `xml:",omitempty"`
	Contributors []*Person           `xml:"contributor,omitempty"`
	Generator    *Generator          `xml:",omitempty"`
	Icon         *Icon               `xml:",omitempty"`
	ID           *ID                 `xml:",omitempty"`
	Links        []*Link             `xml:",omitempty"`
	Logo         *Logo               `xml:",omitempty"`
	Rights       Text                `xml:"rights,omitempty"`
	Subtitle     Text                `xml:"subtitle,omitempty"`
	Title        Text                `xml:"title,omitempty"`
	Updated      *Date               `xml:"updated,omitempty"`
	Extensions   []*ExtensionElement `xml:",any,omitempty"`
}

// NewSource creates a new Source. It returns a *Source.
func NewSource() *Source {
	return &Source{CommonAttributes: NewCommonAttributes()}
}

// AddAuthor adds the Person as an author to the Source. It returns its index as
// an int.
func (s *Source) AddAuthor(p *Person) int {
	return addToSlice(&s.Authors, p)
}

// DeleteAuthor deletes the Person at index from the Source. It return an error.
func (s *Source) DeleteAuthor(index int) error {
	if err := deleteFromSlice(&s.Authors, index); err != nil {
		return fmt.Errorf("error deleting author %v from source %v: %v", index, s, err)
	}
	return nil
}

// AddCategory adds the Category to the Source. It returns its index as an int.
func (s *Source) AddCategory(c *Category) int {
	return addToSlice(&s.Categories, c)
}

// DeleteCategory deletes the Category at index from the Source. It return an
// error.
func (s *Source) DeleteCategory(index int) error {
	if err := deleteFromSlice(&s.Categories, index); err != nil {
		return fmt.Errorf("error deleting category %v from source %v: %v", index, s, err)
	}
	return nil
}

// AddContributor adds the Person as a contributor to the Source. It returns its
// index as an int.
func (s *Source) AddContributor(c *Person) int {
	return addToSlice(&s.Contributors, c)
}

// DeleteContributor deletes the Person at index from the Source. It return an
// error.
func (s *Source) DeleteContributor(index int) error {
	if err := deleteFromSlice(&s.Contributors, index); err != nil {
		return fmt.Errorf("error deleting contributor %v from source %v: %v", index, s, err)
	}
	return nil
}

// AddLink adds the Link to the Source. It returns its index as an int.
func (s *Source) AddLink(l *Link) int {
	return addToSlice(&s.Links, l)
}

// DeleteLink deletes the Link at index from the Source. It return an error.
func (s *Source) DeleteLink(index int) error {
	if err := deleteFromSlice(&s.Links, index); err != nil {
		return fmt.Errorf("error deleting link %v from source %v: %v", index, s, err)
	}
	return nil
}

// AddExtension adds the ExtensionElement to the Source. It returns its index as
// an int.
func (s *Source) AddExtension(e *ExtensionElement) int {
	return addToSlice(&s.Extensions, e)
}

// DeleteExtension deletes the Extension at index from the Source. It return an
// error.
func (s *Source) DeleteExtension(index int) error {
	if err := deleteFromSlice(&s.Extensions, index); err != nil {
		return fmt.Errorf("error deleting extension %v from source %v: %v", index, s, err)
	}
	return nil
}

// Check checks the Source for incompatibilities with RFC4287. It returns an
// error.
func (s *Source) Check() error {
	for i, a := range s.Authors {
		if err := a.Check(); err != nil {
			return fmt.Errorf("author element %v of source %v: %v", i, s, err)
		}
	}

	for i, c := range s.Categories {
		if err := c.Check(); err != nil {
			return fmt.Errorf("category element %v of source %v: %v", i, s, err)
		}
	}

	for i, c := range s.Contributors {
		if err := c.Check(); err != nil {
			return fmt.Errorf("contributor element %v of source %v: %v", i, s, err)
		}
	}

	if s.Generator != nil {
		if err := s.Generator.Check(); err != nil {
			return fmt.Errorf("generator element of source %v: %v", s, err)
		}
	}

	if s.Icon != nil {
		if err := s.Icon.Check(); err != nil {
			return fmt.Errorf("icon element of source %v: %v", s, err)
		}
	}

	if s.ID != nil {
		if err := s.ID.Check(); err != nil {
			return fmt.Errorf("id element of source %v: %v", s, err)
		}
	}

	for i, l := range s.Links {
		if err := l.Check(); err != nil {
			return fmt.Errorf("link element %v of source %v: %v", i, s, err)
		}
	}

	if s.Logo != nil {
		if err := s.Logo.Check(); err != nil {
			return fmt.Errorf("logo element of source %v: %v", s, err)
		}
	}

	if s.Rights != nil {
		if err := s.Rights.Check(); err != nil {
			return fmt.Errorf("rights element of source %v: %v", s, err)
		}
	}

	if s.Subtitle != nil {
		if err := s.Subtitle.Check(); err != nil {
			return fmt.Errorf("subtitle element of source %v: %v", s, err)
		}
	}

	if s.Title != nil {
		if err := s.Title.Check(); err != nil {
			return fmt.Errorf("title element of source %v: %v", s, err)
		}
	}

	if s.Updated != nil {
		if err := s.Updated.Check(); err != nil {
			return fmt.Errorf("updated element of source %v: %v", s, err)
		}
	}

	for i, e := range s.Extensions {
		if err := e.Check(); err != nil {
			return fmt.Errorf("extension element %v of source %v: %v", i, s, err)
		}
	}

	return nil
}