package main

import (
	"errors"
	"fmt"

	"github.com/pborman/uuid"
)

/**
 * Game
 * A team's game, including links, description, and screenshots
 */
type Game struct {
	Name        string
	TeamId      string
	Link        string
	Description string
	Screenshots []Screenshot

	mPath []string // The path in the DB to this game
}

// Create a new game object
func NewGame(tmId string) (*Game, error) {
	if tmId == "" {
		return nil, errors.New("Team ID is required")
	}
	return &Game{
		TeamId: tmId,
		mPath:  []string{"jam", "teams", tmId, "game"},
	}, nil
}

func (gm *Game) GetScreenshot(ssId string) (*Screenshot, error) {
	for _, ss := range gm.Screenshots {
		if ss.UUID == ssId {
			return &ss, nil
		}
	}
	return nil, errors.New("Invalid Id")
}

func (gm *Game) RemoveScreenshot(ssId string) error {
	idx := -1
	for i, ss := range gm.Screenshots {
		if ss.UUID == ssId {
			idx = i
		}
	}
	if idx < 0 {
		return errors.New("Invalid Id")
	}
	fmt.Print("Removing Screenshot (", ssId, ") (IDX:", idx, ")\n")
	gm.Screenshots = append(gm.Screenshots[:idx], gm.Screenshots[idx+1:]...)
	return nil
}

type Screenshot struct {
	UUID        string
	Description string
	Image       string
	Thumbnail   string
	Filetype    string

	mPath []string // The path in the DB to this screenshot
}

// Create a Screenshot Object
func NewScreenshot(tmId, ssId string) (*Screenshot, error) {
	if tmId == "" {
		return nil, errors.New("Team ID is required")
	}
	if ssId == "" {
		// Generate a new UUID
		ssId = uuid.New()
	}
	return &Screenshot{
		UUID:  ssId,
		mPath: []string{"jam", "teams", tmId, "game", "screenshots", ssId},
	}, nil
}

/**
 * DB Functions
 * These are generally just called when the app starts up, or when the periodic 'save' runs
 */

// Load a team's game from the DB and return it
func (gj *Gamejam) LoadTeamGame(tmId string) (*Game, error) {
	var err error
	if err = gj.m.openDB(); err != nil {
		return nil, err
	}
	defer gj.m.closeDB()

	gm, err := NewGame(tmId)
	if err != nil {
		return nil, err
	}
	if gm.Name, err = gj.m.bolt.GetValue(gm.mPath, "name"); err != nil {
		gm.Name = ""
	}
	if gm.Description, err = gj.m.bolt.GetValue(gm.mPath, "description"); err != nil {
		gm.Description = ""
	}
	if gm.Link, err = gj.m.bolt.GetValue(gm.mPath, "link"); err != nil {
		gm.Link = ""
	}

	// Now get the game screenshots
	gm.Screenshots = gj.LoadTeamGameScreenshots(tmId)

	return gm, nil
}

// Load a games screenshots from the DB
func (gj *Gamejam) LoadTeamGameScreenshots(tmId string) []Screenshot {
	var err error
	if err = gj.m.openDB(); err != nil {
		return nil
	}
	defer gj.m.closeDB()

	var ret []Screenshot
	gm, err := NewGame(tmId)
	if err != nil {
		return ret
	}
	ssBktPath := append(gm.mPath, "screenshots")
	var ssIds []string
	ssIds, _ = gj.m.bolt.GetBucketList(ssBktPath)
	for _, v := range ssIds {
		ssLd, _ := gj.LoadTeamGameScreenshot(tmId, v)
		if ssLd != nil {
			ret = append(ret, *ssLd)
		}
	}
	return ret
}

// Load a screenshot from the DB
func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) (*Screenshot, error) {
	var err error
	if err = gj.m.openDB(); err != nil {
		return nil, err
	}
	defer gj.m.closeDB()

	ret, err := NewScreenshot(tmId, ssId)
	if err != nil {
		return nil, err
	}
	if ret.Description, err = gj.m.bolt.GetValue(ret.mPath, "description"); err != nil {
		return nil, err
	}
	if ret.Image, err = gj.m.bolt.GetValue(ret.mPath, "image"); err != nil {
		return nil, err
	}
	if ret.Thumbnail, err = gj.m.bolt.GetValue(ret.mPath, "thumbnail"); err != nil {
		return nil, err
	}
	if ret.Thumbnail == "" {
		ret.Thumbnail = ret.Image
	}
	if ret.Filetype, err = gj.m.bolt.GetValue(ret.mPath, "filetype"); err != nil {
		return nil, err
	}
	return ret, err
}

// Save a game to the DB
func (gj *Gamejam) SaveGame(gm *Game) error {
	var err error
	if err = gj.m.openDB(); err != nil {
		return err
	}
	defer gj.m.closeDB()

	if err := gj.m.bolt.MkBucketPath(gm.mPath); err != nil {
		return err
	}

	var tm *Team
	if tm, err = gj.GetTeamById(gm.TeamId); err != nil {
		return err
	}
	if gm.Name == "" {
		gm.Name = tm.Name + "'s Game"
	}
	if err := gj.m.bolt.SetValue(gm.mPath, "name", gm.Name); err != nil {
		return err
	}
	if err := gj.m.bolt.SetValue(gm.mPath, "link", gm.Link); err != nil {
		return err
	}
	if err := gj.m.bolt.SetValue(gm.mPath, "description", gm.Description); err != nil {
		return err
	}
	if err := gj.m.bolt.MkBucketPath(append(gm.mPath, "screenshots")); err != nil {
		return err
	}
	return gj.SaveScreenshots(gm)
}

// Save all of the game's screenshots to the DB
// Remove screenshots from the DB that aren't in the game object
func (gj *Gamejam) SaveScreenshots(gm *Game) error {
	var err error
	if err = gj.m.openDB(); err != nil {
		return err
	}
	defer gj.m.closeDB()

	for _, ss := range gm.Screenshots {
		if err = gj.SaveScreenshot(&ss); err != nil {
			return err
		}
	}
	// Now remove unused screenshots
	ssPath := append(gm.mPath, "screenshots")
	var ssIds []string
	if ssIds, err = gj.m.bolt.GetBucketList(ssPath); err != nil {
		return err
	}
	for i := range ssIds {
		ss, _ := gm.GetScreenshot(ssIds[i])
		if ss != nil {
			// A valid screenshot, next
			continue
		}
		if ss, err = NewScreenshot(gm.TeamId, ssIds[i]); err != nil {
			// Error building screenshot to delete...
			continue
		}
		if err = gj.DeleteScreenshot(ss); err != nil {
			return err
		}
	}
	return nil
}

// Save a screenshot
func (gj *Gamejam) SaveScreenshot(ss *Screenshot) error {
	var err error
	if err = gj.m.openDB(); err != nil {
		return err
	}
	defer gj.m.closeDB()

	if err = gj.m.bolt.MkBucketPath(ss.mPath); err != nil {
		return err
	}
	if err = gj.m.bolt.SetValue(ss.mPath, "description", ss.Description); err != nil {
		return err
	}
	if err = gj.m.bolt.SetValue(ss.mPath, "image", ss.Image); err != nil {
		return err
	}
	if err = gj.m.bolt.SetValue(ss.mPath, "filetype", ss.Filetype); err != nil {
		return err
	}
	return nil
}

// Delete a screenshot
func (gj *Gamejam) DeleteScreenshot(ss *Screenshot) error {
	var err error
	if err = gj.m.openDB(); err != nil {
		return nil
	}
	defer gj.m.closeDB()

	ssPath := ss.mPath[:len(ss.mPath)-1]
	return gj.m.bolt.DeleteBucket(ssPath, ss.UUID)
}

/**
 * In Memory functions
 * This is generally how the app accesses client data
 */

// Set the given team's game to gm
func (gj *Gamejam) UpdateGame(tmId string, gm *Game) error {
	tm, err := gj.GetTeamById(tmId)
	if err != nil {
		return errors.New("Error getting team: " + err.Error())
	}
	tm.Game = gm
	gj.IsChanged = true
	return nil
}
