package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"sync"
	"time"
)

// MysqlDB needs refacto
type MysqlDB struct {
	Host     string `json:"host"`
	Port     int    `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	DbName   string `json:"db_name"`
}

// CameradarConfig needs refacto
type CameradarConfig struct {
	MysqlDB              MysqlDB `json:"mysql_db"`
	Subnets              string  `json:"subnets"`
	Ports                string  `json:"ports"`
	RtspURLFile          string  `json:"rtsp_url_file"`
	RtspIdsFile          string  `json:"rtsp_ids_file"`
	ThumbnailStoragePath string  `json:"thumbnail_storage_path"`
}

// Result needs refacto
type Result struct {
	Address  string `json:"address"`
	Password string `json:"password"`
	Port     string `json:"port"`
	Route    string `json:"route"`
	Username string `json:"username"`
	Valid    bool   `json:"valid,omitempty"`
	Thumb    string `json:"thumbnail_path,omitempty"`
}

// TestCase needs refacto
type TestCase struct {
	expected []Result
	result   []Result
	time     time.Duration
	ok       bool
}

// Invoke the test
// Wrap results in a TestResult object
func (m *manager) invokeTestCase(testCase *TestCase, wg *sync.WaitGroup) {
	startTime := time.Now()
	if m.runTestCase(testCase) {
		testCase.time = time.Since(startTime)
		testCase.ok = true
		fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds())
	} else {
		testCase.time = time.Since(startTime)
		testCase.ok = false
		fmt.Printf("Test failed in %.6fs\n", testCase.time.Seconds())
	}
	wg.Done()
}

func (m *manager) runTestCase(test *TestCase) bool {
	fmt.Printf("Test triggered\n")

	Cameradar := m.Config.Cameradar
	startService(&Cameradar)

	for Cameradar.Active {
		time.Sleep(5 * time.Millisecond)
	}
	found := 0
	toFind := len(test.expected)
	var validResults []Result
	if getResult(&test.result, "result.json") {
		// Check all valid resutls that are supposed to match
		// Add them to the valid results and leave the failed
		// ones in the expected slice
		for _, r := range test.result {
			index := 0
			r.Valid = true
			for _, e := range test.expected {
				e.Thumb = r.Thumb
				var err error
				var addr []string
				addr, err = net.LookupHost(e.Address)
				e.Address = addr[0]
				if e == r {
					_, err = os.Stat(r.Thumb)
					if err == nil {
						fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.")
						found++
						validResults = Extend(validResults, r)
						test.expected = append(test.expected[:index], test.expected[index+1:]...)
						break
					} else {
						fmt.Println("The result of ", r.Address, " seemed valid, but the thumbnails could not be generated by Cameradar.")
					}
				}
				index++
			}
		}
		index := 0
		// If the result did not match the expected but it was supposed to fail
		// Add it to the valid results and remove it from the expected slice
		for _, e := range test.expected {
			if !e.Valid {
				found++
				validResults = Extend(validResults, e)
				test.expected = append(test.expected[:index], test.expected[index+1:]...)
				break
			}
			index++
		}
		// If we found all the expected results, return true
		if found == toFind {
			return true
		}
		test.result = validResults
	}
	return false
}

func (m *manager) generateConfig(test []Result, DataBase *mysql_db) bool {
	var config CameradarConfig
	var db mysql_db

	db.Host = m.Config.Cameradar.DbHost
	db.Port = m.Config.Cameradar.DbPort
	db.User = m.Config.Cameradar.DbUser
	db.Password = m.Config.Cameradar.DbPassword
	db.Db_name = m.Config.Cameradar.DbName

	for _, t := range test {
		if len(config.Subnets) > 0 {
			config.Subnets += ","
		}
		config.Subnets += t.Address
	}
	config.Mysql_db = db
	config.Ports = m.Config.Cameradar.Ports
	config.Rtsp_url_file = m.Config.Cameradar.RoutesPath
	config.Rtsp_ids_file = m.Config.Cameradar.IdsPath
	config.Thumbnail_storage_path = m.Config.Cameradar.ThumbPath
	b, _ := json.Marshal(config)
	fmt.Println(string(b))
	err := ioutil.WriteFile("tmp_config", b, 0644)
	if err != nil {
		fmt.Println(err)
		return false
	}
	*DataBase = db
	return true
}

// Extend needs refacto
func Extend(slice []Result, element Result) []Result {
	n := len(slice)
	if n == cap(slice) {
		// Slice is full; must grow.
		// We double its size and add 1, so if the size is zero we still grow.
		newSlice := make([]Result, len(slice), 2*len(slice)+1)
		copy(newSlice, slice)
		slice = newSlice
	}
	slice = slice[0 : n+1]
	slice[n] = element
	return slice
}
