//
//  tank
//
//  Import and Playlist Daemon for autoradio project
//
//
//  Copyright (C) 2017-2018 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 (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/jinzhu/gorm"
)

func (p *Playlist) BeforeSave() error {
	for idx := range p.Entries {
		p.Entries[idx].LineNum = uint(idx)
		if p.Entries[idx].File != nil {
			p.Entries[idx].URI = "" // this will be regenerated in AfterFind()
		} else if p.Entries[idx].URI != "" {
			uri, err := url.Parse(p.Entries[idx].URI)
			if err != nil {
				return fmt.Errorf("playlist entry #%d is invalid: %v", idx, err)
			}
			if uri.Scheme == FileURIScheme {
				if uri.Host == "" || uri.Path == "" {
					return fmt.Errorf("playlist entry #%d is invalid: uri must be in the format %s://<group>/<file-id>", idx, FileURIScheme)
				}
				fileID, err := strconv.ParseUint(strings.TrimPrefix(uri.Path, "/"), 10, 64)
				if err != nil {
					return fmt.Errorf("playlist entry #%d is invalid: %v", idx, err)
				}
				p.Entries[idx].File = &File{ID: fileID, GroupName: uri.Host}
				p.Entries[idx].URI = "" // this will be regenerated in AfterFind()
			}
		} else {
			return fmt.Errorf("playlist entry #%d is invalid, entries must either contain a File or have a URI set", idx)
		}
	}
	return nil
}

func (p *Playlist) AfterFind() error {
	for idx := range p.Entries {
		if p.Entries[idx].File != nil {
			urihost := p.Entries[idx].File.GroupName
			uripath := strconv.FormatUint(p.Entries[idx].File.ID, 10)
			uri := url.URL{Scheme: FileURIScheme, Host: urihost, Path: uripath}
			p.Entries[idx].URI = uri.String()
		}
	}
	return nil
}

func (st *Store) ListPlaylists(group string) (playlists Playlists, err error) {
	err = st.db.Where("group_name = ?", group).Preload("Entries", func(db *gorm.DB) *gorm.DB {
		return db.Order("playlist_entries.line_num asc")
	}).Preload("Entries.File").Find(&playlists).Error
	return
}

func (st *Store) CreatePlaylist(group string, playlist Playlist) (*Playlist, error) {
	if _, err := st.CreateGroup(group); err != nil {
		return nil, err
	}
	playlist.ID = 0
	playlist.GroupName = group
	err := st.db.Create(&playlist).Error
	// TODO: the returned playlist object will not contain properly initialized File Objects
	return &playlist, err
}

func (st *Store) GetPlaylist(group string, id uint64) (playlist *Playlist, err error) {
	playlist = &Playlist{}
	// we have to make sure that the playlist actually belongs to <group>
	// otherwise a bad user can trick us into returning playlists for groups it
	// normally has no access to
	err = st.db.Where("group_name = ?", group).Preload("Entries", func(db *gorm.DB) *gorm.DB {
		return db.Order("playlist_entries.line_num ASC")
	}).Preload("Entries.File").First(playlist, id).Error
	return
}

func (st *Store) UpdatePlaylist(group string, id uint64, playlist Playlist) (*Playlist, error) {
	// TODO: implement this
	return nil, ErrNotImplented
}

func (st *Store) DeletePlaylist(group string, id uint64) (err error) {
	tx := st.db.Begin()
	if err = tx.First(&Playlist{}, id).Error; err != nil {
		tx.Rollback()
		return
	}
	if err = tx.Delete(&Playlist{ID: id}).Error; err != nil {
		tx.Rollback()
		return
	}
	tx.Commit()
	return
}