package darwin

import (
	"encoding/xml"
	"time"
)

type QueryTimetable struct {
	//XMLName xml.Name `xml:"QueryTimetable"`
}

type TimetableID struct {
	//XMLName           xml.Name `xml:"TimeTableId"`
	ID                string `xml:",chardata"`
	Filename          string `xml:"ttfile,attr"`
	ReferenceFilename string `xml:"ttreffile,attr"`
}

type GetSnapshotReq struct {
	//XMLName xml.Name `xml:"GetSnapshotReq"`
	ViaFTP bool `xml:"viaftp,attr"`
}

type GetFullSnapshotReq struct {
	//XMLName xml.Name `xml:"GetFullSnapshotReq"`
	ViaFTP bool `xml:"viaftp,attr"`
}

type SnapshotID struct {
	//XMLName xml.Name `xml:"SnapshotId"`
	ID string `xml:",chardata"`
}

type StartUpdateReq struct {
	//XMLName xml.Name `xml:"StartUpdateReq"`
}

type StopUpdateReq struct {
	//XMLName xml.Name `xml:"StopUpdateReq"`
}

type FailureResp struct {
	//XMLName       xml.Name `xml:"FailureResp"`
	ErrorMessage  string `xml:",chardata"`
	Code          string `xml:"code,attr"`
	RequestSource string `xml:"requestSource,attr"`
	RequestID     string `xml:"requestID,attr"`
}

type ScheduleCallingPoint struct {
	XMLName xml.Name

	TIPLOC    string `xml:"tpl,attr"`
	Act       string `xml:"act,attr"`
	PlanAct   string `xml:"planAct,attr"`
	Platform  string `xml:"plat,attr"`
	Cancelled bool   `xml:"can,attr"`
	FID       string `xml:"fid,attr"`

	PTA        string `xml:"pta,attr"`
	PTD        string `xml:"ptd,attr"`
	AvgLoading int    `xml:"avgLoading,attr"`

	WTA string `xml:"wta,attr"`
	WTD string `xml:"wtd,attr"`
	WTP string `xml:"wtp,attr"`
	FD  string `xml:"fd,attr"`

	RDelay int `xml:"rdelay,attr"`
}

type Schedule struct {
	//XMLName xml.Name `xml:"schedule"`

	CallingPoints      []ScheduleCallingPoint `xml:",any"`
	CancellationReason *DisruptionReason      `xml:"cancelReason" json:",omitempty"`

	RID            string  `xml:"rid,attr"`
	UID            string  `xml:"uid,attr"`
	TrainID        string  `xml:"trainId,attr"`
	RSID           string  `xml:"rsid,attr"`
	SSD            string  `xml:"ssd,attr"` // RTTIDateType
	TOC            string  `xml:"toc,attr"`
	Status         *string `xml:"status,attr"`
	TrainCat       *string `xml:"trainCat,attr"`
	IsPassengerSvc *bool   `xml:"isPassengerSvc,attr"`
	IsActive       *bool   `xml:"isActive,attr"`
	Deleted        bool    `xml:"deleted,attr"`
	IsCharter      *bool   `xml:"isCharter,attr"`
	IsQTrain       *bool   `xml:"qtrain,attr"`
	Cancelled      *bool   `xml:"can,attr"`
}

func (s Schedule) AffectedRIDs() []string {
	return []string{s.RID}
}

type DeactivatedSchedule struct {
	//XMLName xml.Name `xml:"deactivated"`

	RID string `xml:"rid,attr"`
}

func (ds DeactivatedSchedule) AffectedRIDs() []string {
	return []string{ds.RID}
}

type CircularTimes struct {
	WTA string `xml:"wta,attr" json:",omitempty"`
	WTD string `xml:"wtd,attr" json:",omitempty"`
	WTP string `xml:"wtp,attr" json:",omitempty"`
	PTA string `xml:"pta,attr" json:",omitempty"`
	PTD string `xml:"ptd,attr" json:",omitempty"`
}

type AssocService struct {
	RID string `xml:"rid,attr"`
	CircularTimes
}

type Association struct {
	//XMLName xml.Name `xml:"association"`

	Main  AssocService `xml:"main"`
	Assoc AssocService `xml:"assoc"`

	TIPLOC      string `xml:"tiploc,attr"`
	Category    string `xml:"category,attr"`
	IsCancelled bool   `xml:"isCancelled,attr"`
	IsDeleted   bool   `xml:"isDeleted,attr"`
}

func (a Association) AffectedRIDs() []string {
	return []string{a.Main.RID, a.Assoc.RID}
}

type ToiletAvailability struct {
	Type   string `xml:",chardata"`
	Status string `xml:"status,attr"`
}

type Coach struct {
	Toilet []ToiletAvailability `xml:"toilet"`

	CoachNumber string `xml:"coachNumber,attr"`
	CoachClass  string `xml:"coachClass,attr"`
}

type Formation struct {
	//XMLName xml.Name `xml:"formation"`

	Coaches []Coach `xml:"coaches>coach"`

	FID     string `xml:"fid,attr"`
	Src     string `xml:"src,attr"`
	SrcInst string `xml:"srcInst,attr"`
}

type ScheduleFormation struct {
	//XMLName xml.Name `xml:"scheduleFormations"`

	Formation []Formation `xml:"formation"`

	RID string `xml:"rid,attr"`
}

func (sf ScheduleFormation) AffectedRIDs() []string {
	return []string{sf.RID}
}

type DisruptionReason struct {
	Code   string `xml:",chardata"`
	TIPLOC string `xml:"tiploc,attr"`
	Near   bool   `xml:"near,attr"`
}

type TSTimeData struct {
	ET        string `xml:"et,attr" json:",omitempty"`
	WET       string `xml:"wet,attr" json:",omitempty"`
	AT        string `xml:"at,attr" json:",omitempty"`
	ATRemoved bool   `xml:"atRemoved,attr" json:",omitempty"`
	ETMin     string `xml:"etmin,attr" json:",omitempty"`
	ETUnknown bool   `xml:"etUnknown,attr" json:",omitempty"`
	Delayed   bool   `xml:"delayed,attr" json:",omitempty"`
	Src       string `xml:"src,attr" json:",omitempty"`
	SrcInst   string `xml:"srcInst,attr" json:",omitempty"`
}

type PlatformData struct {
	Platform   string `xml:",chardata"`
	PlatSup    bool   `xml:"platsup,attr"`
	CisPlatSup bool   `xml:"cisPlatsup,attr"`
	PlatSrc    string `xml:"platsrc,attr"`
	Conf       bool   `xml:"conf,attr"`
}

type TSLocation struct {
	TIPLOC string `xml:"tpl,attr"`
	CircularTimes

	Arr         *TSTimeData   `xml:"arr" json:",omitempty"`
	Dep         *TSTimeData   `xml:"dep" json:",omitempty"`
	Pass        *TSTimeData   `xml:"pass" json:",omitempty"`
	Plat        *PlatformData `xml:"plat" json:",omitempty"`
	Suppr       *bool         `xml:"suppr" json:",omitempty"`
	Length      *int          `xml:"length" json:",omitempty"`
	DetachFront *bool         `xml:"detachFront" json:",omitempty"`
}

type TrainStatus struct {
	//XMLName xml.Name `xml:"TS"`

	LateReason *DisruptionReason `xml:"LateReason" json:",omitempty"`
	Location   []TSLocation      `xml:"Location"`

	RID                string `xml:"rid,attr"`
	UID                string `xml:"uid,attr"`
	SSD                string `xml:"ssd,attr"`
	IsReverseFormation bool   `xml:"isReverseFormation,attr"`
}

func (ts TrainStatus) AffectedRIDs() []string {
	return []string{ts.RID}
}

type CoachLoadingData struct {
	Value string `xml:",chardata"`

	CoachNumber string `xml:"coachNumber,attr"`
	Src         string `xml:"src,attr"`
	SrcInst     string `xml:"srcInst,attr"`
}

type FormationLoading struct {
	//XMLName xml.Name `xml:"formationLoading"`

	Loading []CoachLoadingData `xml:"loading"`

	FID    string `xml:"fid,attr"`
	RID    string `xml:"rid,attr"`
	TIPLOC string `xml:"tpl,attr"`
	CircularTimes
}

func (fl FormationLoading) AffectedRIDs() []string {
	return []string{fl.RID}
}

type StationMessageStation struct {
	CRS string `xml:"crs,attr"`
}

type StationMessageMessage struct {
	InnerXML string `xml:",innerxml"`
}

type StationMessage struct {
	Station []StationMessageStation `xml:"Station"`
	Msg     StationMessageMessage   `xml:"Msg"`

	ID       int    `xml:"id,attr"`
	Cat      string `xml:"cat,attr"`
	Sev      string `xml:"sev,attr"`
	Suppress bool   `xml:"bool,attr"`
}

type AlertService struct {
	RID string `xml:"RID,attr"`
	UID string `xml:"UID,attr"`
	SSD string `xml:"SSD,attr"`

	Location []string `xml:"Location"`
}

type TrainAlert struct {
	AlertID            int            `xml:"AlertID"`
	AlertServices      []AlertService `xml:"AlertServices"`
	SendAlertBySMS     bool           `xml:"SendAlertBySMS"`
	SendAlertByEmail   bool           `xml:"SendAlertByEmail"`
	SendAlertByTwitter bool           `xml:"SendAlertByTwitter"`
	Source             string         `xml:"Source"`
	AlertText          string         `xml:"AlertText"`
	Audience           string         `xml:"Audience"`
	AlertType          string         `xml:"AlertType"`
}

func (ta TrainAlert) AffectedRIDs() []string {
	rids := make([]string, len(ta.AlertServices))
	for n, as := range ta.AlertServices {
		rids[n] = as.RID
	}
	return rids
}

type TrainOrderRID struct {
	RID string `xml:",chardata"`
	CircularTimes
}

type TrainOrderItem struct {
	RID     *TrainOrderRID `xml:"rid"`
	TrainID *string        `xml:"trainID"`
}

type TrainOrderData struct {
	First  TrainOrderItem  `xml:"first"`
	Second *TrainOrderItem `xml:"second"`
	Third  *TrainOrderItem `xml:"third"`
}

type TrainOrder struct {
	Set   *TrainOrderData `xml:"set"`
	Clear struct{}        `xml:"clear"`

	TIPLOC   string `xml:"tiploc,attr"`
	CRS      string `xml:"crs,attr"`
	Platform string `xml:"platform,attr"`
}

type FullTDBerthID struct {
	Berth string `xml:",chardata"`

	Area string `xml:"area,attr"`
}

type TrackingID struct {
	Berth            FullTDBerthID `xml:"berth"`
	IncorrectTrainID string        `xml:"incorrectTrainID"`
	CorrectTrainID   string        `xml:"correctTrainID"`
}

type RTTIAlarmData struct {
	TDAreaFail     *string `xml:"tdAreaFail"`
	TDFeedFail     *string `xml:"tdFeedFail"`
	TyrellFeedFail *string `xml:"tyrellFeedFail"`

	ID string `xml:"id,attr"`
}

type RTTIAlarm struct {
	Set   *RTTIAlarmData `xml:"set"`
	Clear *string        `xml:"clear"`
}

type DataResponse struct {
	Schedule           []Schedule            `xml:"schedule" json:",omitempty"`
	Deactivated        []DeactivatedSchedule `xml:"deactivated" json:",omitempty"`
	Association        []Association         `xml:"association" json:",omitempty"`
	ScheduleFormations []ScheduleFormation   `xml:"scheduleFormations" json:",omitempty"`
	TrainStatus        []TrainStatus         `xml:"TS" json:",omitempty"`
	FormationLoading   []FormationLoading    `xml:"formationLoading" json:",omitempty"`
	OW                 []StationMessage      `xml:"OW" json:",omitempty"`
	TrainAlert         []TrainAlert          `xml:"trainAlert" json:",omitempty"`
	TrainOrder         []TrainOrder          `xml:"trainOrder" json:",omitempty"`
	TrackingID         []TrackingID          `xml:"trackingID" json:",omitempty"`
	Alarm              []RTTIAlarm           `xml:"alarm" json:",omitempty"`
}

func (r DataResponse) AffectedRIDs() []string {
	var rids []string
	for _, s := range r.Schedule {
		rids = append(rids, s.AffectedRIDs()...)
	}
	for _, ds := range r.Deactivated {
		rids = append(rids, ds.AffectedRIDs()...)
	}
	for _, a := range r.Association {
		rids = append(rids, a.AffectedRIDs()...)
	}
	for _, sf := range r.ScheduleFormations {
		rids = append(rids, sf.AffectedRIDs()...)
	}
	for _, ts := range r.TrainStatus {
		rids = append(rids, ts.AffectedRIDs()...)
	}
	for _, fl := range r.FormationLoading {
		rids = append(rids, fl.AffectedRIDs()...)
	}
	// OW doesn't affect RIDs directly.
	for _, ta := range r.TrainAlert {
		rids = append(rids, ta.AffectedRIDs()...)
	}
	// TrainOrder needs extra processing (clear doesn't specify anything so we have to know ahead of time...)
	// TrackingID doesn't contain RIDs.
	// Alarm doesn't affect RIDs directly.
	return rids
}

type UpdateResp struct {
	UpdateOrigin  string `xml:"updateOrigin,attr"`
	RequestSource string `xml:"requestSource,attr" json:",omitempty"`
	RequestID     string `xml:"requestID,attr" json:",omitempty"`
	DataResponse
}

type SnapshotResp struct {
	DataResponse
}

type PushPort struct {
	//XMLName xml.Name `xml:"Pport"`

	// Subelements
	QueryTimetable     []QueryTimetable     `xml:"QueryTimetable" json:",omitempty"`
	TimeTableID        []TimetableID        `xml:"TimeTableId" json:",omitempty"`
	GetSnapshotReq     []GetSnapshotReq     `xml:"GetSnapshotReq" json:",omitempty"`
	GetFullSnapshotReq []GetFullSnapshotReq `xml:"GetFullSnapshotReq" json:",omitempty"`
	SnapshotID         []SnapshotID         `xml:"SnapshotId" json:",omitempty"`
	StartUpdateReq     []StartUpdateReq     `xml:"StartUpdateReq" json:",omitempty"`
	StopUpdateReq      []StopUpdateReq      `xml:"StopUpdateReq" json:",omitempty"`
	FailureResp        []FailureResp        `xml:"FailureResp" json:",omitempty"`
	UpdateResp         []UpdateResp         `xml:"uR" json:",omitempty"`
	SnapshotResp       []SnapshotResp       `xml:"sR" json:",omitempty"`

	// Attrs
	Time time.Time `xml:"ts,attr"`
}

func (pp PushPort) AffectedRIDs() []string {
	var rids []string
	for _, r := range pp.UpdateResp {
		rids = append(rids, r.AffectedRIDs()...)
	}
	for _, r := range pp.SnapshotResp {
		rids = append(rids, r.AffectedRIDs()...)
	}
	return rids
}

type PushPortTimetable struct {
	Journey     []Schedule    `xml:"Journey"`
	Association []Association `xml:"Association"`

	// Attrs
	TimetableID string `xml:"timetableID,attr"`
}