//
//  tank
//
//  Import and Playlist Daemon for autoradio project
//
//
//  Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at>
//
//  This file is part of tank.
//
//  tank is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  any later version.
//
//  tank is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with tank. If not, see <http://www.gnu.org/licenses/>.
//

package store

import (
	"errors"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
)

const (
	FileURIScheme = "file"

	// well-known table names
	migrationsTn = "__migrations__"

	// fixtures
	publicShowName = "public"
)

//******* Errors

// TODO: make all errors types???

var (
	ErrNotImplemented    = errors.New("not implemented")
	ErrNotFound          = gorm.ErrRecordNotFound
	ErrFileImportNotDone = errors.New("file import is not done")
)

type ErrFileInUse struct {
	Playlists Playlists `json:"playlists"`
}

func (e *ErrFileInUse) Error() string {
	return fmt.Sprintf("the file is still in use by %d playlist(s)", len(e.Playlists))
}

type ErrInvalidMetadataField string

func (e ErrInvalidMetadataField) Error() string {
	return "invalid metadata field: " + string(e)
}

//******* Shows

type Show struct {
	Name      string    `json:"name" gorm:"primary_key"`
	CreatedAt time.Time `json:"created"`
	UpdatedAt time.Time `json:"updated"`
}

func (g Show) String() string {
	return g.Name
}

type Shows []Show

//******* Files

type ImportState int

const (
	ImportNew ImportState = iota
	ImportPending
	ImportRunning
	ImportDone
	ImportAborted
)

func (s ImportState) String() string {
	switch s {
	case ImportNew:
		return "new"
	case ImportPending:
		return "pending"
	case ImportRunning:
		return "running"
	case ImportDone:
		return "done"
	case ImportAborted:
		return "aborted"
	}
	return "unknown"
}

func (s *ImportState) fromString(str string) error {
	switch str {
	case "new":
		*s = ImportNew
	case "pending":
		*s = ImportPending
	case "running":
		*s = ImportRunning
	case "done":
		*s = ImportDone
	case "aborted":
		*s = ImportAborted
	default:
		return errors.New("invalid import state: '" + str + "'")
	}
	return nil
}

func (s ImportState) MarshalText() (data []byte, err error) {
	data = []byte(s.String())
	return
}

func (s *ImportState) UnmarshalText(data []byte) (err error) {
	return s.fromString(string(data))
}

// type ImportLogLine struct {
// 	Timestamp time.Time `json:"timestamp"`
// 	Message   string    `json:"message"`
// }

// type ImportLog []ImportLogLine

// func (l ImportLog) Append(message string) {
// 	l = append(l, ImportLogLine{time.Now(), message})
// }

type Import struct {
	State ImportState `json:"state"`
	//	Log     ImportLog   `json:"log"`
}

type FileSource struct {
	URI    string `json:"uri" gorm:"size:1024"`
	Hash   string `json:"hash"`
	Import Import `json:"import" gorm:"embedded;embedded_prefix:import__"`
}

type FileMetadata struct {
	// TODO: actually a full-text index would be nice here...
	Artist string `json:"artist,omitempty" gorm:"index"`
	Title  string `json:"title,omitempty" gorm:"index"`
	Album  string `json:"album,omitempty" gorm:"index"`
	// TODO: add more fields
}

type File struct {
	ID        uint64        `json:"id" gorm:"primary_key"`
	CreatedAt time.Time     `json:"created"`
	UpdatedAt time.Time     `json:"updated"`
	ShowName  string        `json:"show" gorm:"not null;index"`
	Show      Show          `json:"-" gorm:"association_foreignkey:Name"`
	Source    FileSource    `json:"source" gorm:"embedded;embedded_prefix:source__"`
	Metadata  FileMetadata  `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
	Size      uint64        `json:"size"`
	Duration  time.Duration `json:"duration"`
}

type Files []File

//******* Playlists

type PlaylistEntry struct {
	ID         uint64  `json:"-" gorm:"primary_key"`
	PlaylistID uint64  `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
	LineNum    uint    `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
	URI        string  `json:"uri" gorm:"size:1024"`
	File       *File   `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
	FileID     *uint64 `json:"-" gorm:"index"`
}

type Playlist struct {
	ID          uint64          `json:"id" gorm:"primary_key"`
	CreatedAt   time.Time       `json:"created"`
	UpdatedAt   time.Time       `json:"updated"`
	Description string          `json:"description"`
	ShowName    string          `json:"show" gorm:"not null;index"`
	Show        Show            `json:"-" gorm:"association_foreignkey:Name"`
	Entries     []PlaylistEntry `json:"entries,omitempty"`
}

type Playlists []Playlist

//****************