package martilq


import (
	"fmt"
	"os"
	"path/filepath"
	"encoding/json"
	"io/ioutil"
	"github.com/google/uuid"
	"math"
	"time"
	"errors"
	"strings"
	"strconv"
	"bufio"
	"log"
	"reflect"
	"regexp"
)



type Marti struct {
	Content_type string `json:"contentType"`
	Title string `json:"title"`
	Uid string `json:"uid"`

	Description string `json:"description"`
	Modified string `json:"modified"`
	Publisher string `json:"publisher"`
	ContactPoint string `json:"contactPoint"`
	AccessLevel string `json:"accessLevel"`
	Rights string `json:"rights"`
	Tags []string `json:"tags"`
	License string `json:"license"`
	State string `json:"state"`
	Batch float64 `json:"batch"`
	DescribedBy string `json:"describedBy"`
	LandingPage string `json:"landingPage"`
	Theme string `json:"theme"`

	Resources []Resource `json:"resources"`
	Custom []interface{} `json:"custom"`


	config configuration
}

func NewMarti() Marti {

	m := Marti {}
	m.Content_type = "application/vnd.martilq.json"
	u := uuid.New()
	m.Uid = u.String()

	software := GetSoftware()
	m.Custom = append(m.Custom, software)

	m.config = NewConfiguration()
	m.Modified = time.Now().Format(m.config.dateTimeFormat)

	return m
}

func Load(c configuration, pathFile string) (Marti, error) {
	
	m := Marti {}

	data, err := ioutil.ReadFile(pathFile)
    if err != nil {
	  return m, err
    }

    err = json.Unmarshal(data, &m)
    if err != nil {
        fmt.Println("error:", err)
		return m, err
    }

	if reflect.TypeOf(c) == reflect.TypeOf(m.config) {
		m.config = c
	}

	return m, nil
}

func (m *Marti) LoadConfig(ConfigPath string) {
	m.config.LoadConfig(ConfigPath)

	m.Publisher = m.config.publisher
	m.ContactPoint = m.config.contactPoint
	m.AccessLevel = m.config.accessLevel
	m.Modified = time.Now().Format(m.config.dateTimeFormat)
	m.State = m.config.state
	m.Rights = m.config.rights
	if m.config.tags != "" {
		m.Tags = strings.Split(m.config.tags, ",")
	}
	m.Theme = m.config.theme
	m.License = m.config.license
	if m.config.batch != "" {
		if m.config.batch[0] == '@' {
			_, err := os.Stat(m.config.batch[1:])
			if os.IsNotExist(err) {
				// See if we can locate it in Config INI directory
				_, fileb := filepath.Split(m.config.batch[1:])
				dirc, _ := filepath.Split(ConfigPath)
				_, err := os.Stat(dirc + fileb)
				if err == nil {
					m.config.batch = "@" + dirc + fileb
				}
			}
			_, err = os.Stat(m.config.batch[1:])
			if os.IsNotExist(err) {
				WriteLog(fmt.Sprintf("Batch file '%v' does not exist" , m.config.batch))		
			} else {
				readFile, err := os.Open(m.config.batch[1:])
				if err != nil {
					log.Fatalf("failed to open file: %s", err)
				}
				reader := bufio.NewReader(readFile)
				var line string
				line, _ = reader.ReadString('\n')
				m.Batch, _ = strconv.ParseFloat(line, 10)
				readFile.Close()
			}
			
		} else {
			m.Batch, _ = strconv.ParseFloat(m.config.batch, 10)
		}
	}

	if m.config.spatial.enabled == true {
		spatial := m.config.spatial
		m.Custom = append(m.Custom, spatial)
	}
	if m.config.temporal.enabled == true {
		temporal := m.config.temporal
		m.Custom = append(m.Custom, temporal)
	}

}


func (m *Marti) AddResource(SourcePath string, Url string) (Resource, error) {

	r, err := NewMartiLQResource(m.config, SourcePath, Url, false, true)
	if err != nil {
		return r, errors.New("Error in adding resource: "+SourcePath)
	}
	
	// Find if we already have the resource
	// This can occur if we are reloading
	matched := false
	for ix := 0; ix < len(m.Resources); ix++ {
		if m.Resources[ix].DocumentName == r.DocumentName && m.Resources[ix].Url == r.Url {
			m.Resources[ix] = r
			matched = true
			break
		}		
    }

	if !matched {
		m.Resources = append(m.Resources, r)
	}

	return r, nil
}

func (m *Marti) Save(pathFile string) bool {

	if pathFile == "" {
		return false
	}

	j, err := json.MarshalIndent(m, "","    ")
	if err != nil {
		fmt.Println(err)
		return false
	} else {
		_ = ioutil.WriteFile(pathFile, j, 0644)
	}

	return true
}


func Make(ConfigPath string, SourcePath string, Filter string, Recursive bool, UrlPrefix string, DefinitionPath string) Marti {

	m := NewMarti()

	if DefinitionPath != "" {
		_, err := os.Stat(DefinitionPath)
		if err == nil {
			m, err = Load(m.config, DefinitionPath)
			if err != nil {
				panic("Unable to load existing MartiLQ definition: " + DefinitionPath)
			}
			// Update the batch number, minor version
			m.Batch = math.Round((m.Batch + m.config.batchInc)/m.config.batchInc)*m.config.batchInc
			m.config.LoadConfig(ConfigPath)
		}  else {
			if ConfigPath != "" {
				m.LoadConfig(ConfigPath)
			}	
		}
	} else {
		if ConfigPath != "" {
			m.LoadConfig(ConfigPath)
		}
	}

	fileStat, err := os.Stat(SourcePath)
	fileAbs, err :=filepath.Abs(SourcePath)
	if err != nil {
		panic("Source path does not exist or is inaccessible: " + SourcePath)
	} else {

		if UrlPrefix == "" {
			UrlPrefix = m.config.urlPrefix
		}
		if UrlPrefix == "" {
			UrlPrefix = "file://"
		}

		if fileStat.IsDir() {
			diffCheck := fileAbs+string(os.PathSeparator)

			filepath.Walk(fileAbs, func(path string, info os.FileInfo, err error) error {
				if err != nil {
					log.Fatalf(err.Error())
					return nil
				}
				if info.IsDir() == false {
					diff := strings.Replace(path, diffCheck, "", -1)
					if Recursive || diff == info.Name() {

						found := true 
						if Filter != "" {
							found, err = regexp.MatchString(Filter, info.Name())
							if err != nil {
								log.Fatal(err)
							} 
						}

						if found {
							url := UrlPrefix+strings.Replace(diff, "\\", "/", -1)
							if UrlPrefix[0:6] == "file://" || UrlPrefix[0:1] == "\\\\" {
								url = UrlPrefix+diff
							}
							m.AddResource(path, url) 
						}
					} else {
						return filepath.SkipDir
					}
				}
				return nil
			})
		} else {
			url := UrlPrefix+fileStat.Name()
			m.AddResource(fileAbs, url) 
		}
	}

	return m

}


func ReconcileFilePath(ConfigPath string, SourcePath string, Recursive bool, DefinitionPath string, OutputPath string) Marti {

	m := NewMarti()


	return m
}
