/**
= Creative Commons Lizenzvertrag =
Diese Software ist von der archium GmbH, Gera ist lizenziert unter einer Creative Commons Namensnennung - Nicht kommerziell - Keine Bearbeitungen 4.0 International Lizenz. (http://creativecommons.org/licenses/by-nc-nd/4.0/deed.de)
Individuelle über diese Lizenz hinausgehende Berechtigungen können Sie unter https://archium.org erhalten.

= Creative Commons License =
Software by archium GmbH, Gera is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. (http://creativecommons.org/licenses/by-nc-nd/4.0/)
Individual permissions beyond the scope of this license may be available at https://archium.org.
**/

package tools

import (
	"encoding/json"
	"log"

	"errors"
	// "fmt"
	"html"
	"net/http"

	"reflect"
	"regexp"
	"strings"
	"time"

	def "Toolbox/defaults"
	pog "Toolbox/postgres"
	sql "database/sql"

	gjs "github.com/tidwall/gjson"
)

type TJson []map[string]interface{}

type TDVParams struct {
	DP_datasetId      string //
	DP_datasetPid     string //
	DP_datasetVersion string //
	DP_localeCode     string //
	DP_fileId         string //
	DP_filePid        string //
	DP_apiKey         string //
	DP_siteUrl        string //
	DP_alias          string //
	DP_routeUrl       string //
}

type TFVParams struct {
	FP_suid  string // generierte suid (js)
	FP_type  string // 'json', 'html'..
	FP_info  string // optionale info (bsp.: 'read json grid')
	FP_func  string // 'getHtml', 'getValues'..
	FP_modl  string // 'app.active_module' (js)
	FP_what  string // 'menu', 'datagrid', 'propgrid', 'login'..
	FP_suff  string // 'login'
	FP_extn  string // '.txt'. '.json'..
	FP_from  string // 'db', 'file', 'dataverse'..
	FP_user  string //
	FP_proj  string // 'projektname aus tabelle'
	FP_who   string // art der daten - 'user', 'data'...
	FP_time  string // übergabe zeitpunkt.
	FP_alias string // alias für dataverse grid
	FP_dois  string // liste der "dois" für sicherung
	FP_rout  string // umleitung beim ausloggen
	FP_refr  bool   // refresh der session möglich
	// für svg
	FP_shad string // svg-schatten
	FP_widt string // svg-breite
	FP_heit string // svg-höhe
	FP_matx string // svg-transfor.matrix
	FP_view string // svg-viewbox
	//
	FP_qery string // abfrage-string
	//
	FP_parm ColMapInt
}

type TFileColumns struct {
	FC_sequence  string
	FC_field     string
	FC_title     string
	FC_isvisible bool
}

type TValueRow struct {
	VR_key   string
	VR_value string
}

type TUserEntry struct {
	UE_Done      bool
	UE_IpAddress string
	UE_IpPort    string
	UE_IdleTime  time.Time
	UE_Released  time.Time
	UE_DVParams  TDVParams
	UE_FVParams  TFVParams
	UE_DBConn    *sql.DB
	UE_Datasets  string
}

type SVGMap map[string]string
type ColMap map[string]string
type JsnColMap map[string]interface{}
type ColMapInt map[int]map[string]string
type RowMap map[string]map[string]string
type JsnMapInt map[int]map[string]interface{}
type TUserEntries map[string]TUserEntry
type TDatasetEntries map[string]string

type TDataverse JsnColMap

// dataverse
var Dp TDVParams
var Fp TFVParams
var Ue TUserEntries
var Ds TDatasetEntries

var icnt int = 0
var Col = ""
var Val = ""

var Source string = "head;menu;grid;propgrid;content;login;"

//
var DataColList ColMap = make(ColMap, 0)
var FileColList ColMap = make(ColMap, 0)

func CheckConnection(_fparam TFVParams) (*sql.DB, error) {
	var err error = errors.New("SUID not found.")
	if len(_fparam.FP_suid) > 0 {
		var ok bool = false
		var ue TUserEntry
		//
		ue, ok = Ue[_fparam.FP_suid]
		if ok {
			// fmt.Println("CheckConnection(entry):", _fparam.FP_suid, ue.UE_DBConn)
			if ue.UE_DBConn == nil {
				ue.UE_DBConn, err = pog.GetConnection()
				if err == nil {
					//fmt.Println("CheckConnection(created 1):", _fparam.FP_suid, ue.UE_DBConn)
					Ue[_fparam.FP_suid] = ue
					return ue.UE_DBConn, nil
				}
			} else {
				return ue.UE_DBConn, nil
			}
		}
	}
	//
	return nil, err
}

func CloseConnection(_fparam TFVParams) error {
	// var err error = errors.New("SUID not found.")
	var ue TUserEntry = Ue[_fparam.FP_suid]
	pog.CloseConnection(ue.UE_DBConn)
	//
	return nil
}

func RemoveLBR(text string, repl string) string {
	re := regexp.MustCompile(`\x{000D}\x{000A}|[\x{000A}\x{000B}\x{000C}\x{000D}\x{0085}\x{2028}\x{2029}]`)
	return re.ReplaceAllString(text, repl)
}

func PrintMap(_map map[string][]string) {
	for key, value := range _map {
		for fkey, fvalue := range value {
			log.Println(key, " has ", fkey, ":", fvalue)
		}
	}
}

func GetIP(_r *http.Request) string {
	forwarded := _r.Header.Get("X-FORWARDED-FOR")
	if forwarded != "" {
		return forwarded
	}
	return _r.RemoteAddr
}

func GetApiKey(_dp TDVParams) string {
	var apikey string = def.DEF_apikey
	if len(_dp.DP_apiKey) > 0 {
		apikey = _dp.DP_apiKey
	}
	//
	return apikey
}

func GetSiteUrl(_dp TDVParams) string {
	var siteurl string = def.DEF_siteurl
	if len(_dp.DP_siteUrl) > 0 {
		siteurl = _dp.DP_siteUrl
	}
	//
	return siteurl
}

func GetRouteUrl(_dp TDVParams) string {
	var siteurl string = def.DEF_routeurl
	if len(_dp.DP_routeUrl) > 0 {
		siteurl = _dp.DP_routeUrl
	}
	//
	return siteurl
}

// get string between (tools)
func GetstringBetween(_string string, _start string, _end string) string {
	var str = "" + _string
	var s = strings.Index(str, _start)
	if s == -1 {
		return ""
	}
	s += len(_start)
	e := strings.Index(str, _end)
	if e == -1 {
		return ""
	}
	return str[s:e]
}

func AddStrings(_sum, _val string, _sep string) string {
	if len(_sum) > 0 {
		return _sum + _sep + _val
	}
	return _val
}

func UniqueStrings(_str, _sep string) string {
	m := make(map[string]bool)
	keys := make([]string, 0)
	for _, c := range strings.Split(_str, _sep) {
		if _, ok := m[c]; !ok {
			m[c] = true
			keys = append(keys, c)
		}
	}
	//
	return strings.Join(keys, _sep)
}

func JsonEscape(i string) string {
	b, err := json.Marshal(i)
	if err != nil {
		def.LogError("JsonEscape()", err)
		panic(err)
	}
	s := string(b)
	//
	return s[1 : len(s)-1]
}

func GetTabLevel(_level int64) string {
	var tab string = ""
	for i := 0; i < int(_level); i++ {
		tab = tab + "\t"
	}
	//
	return tab
}

func GetTab10(_level int) (string, string) {
	var taba string = "\t"
	var tabb string = "\t\t"
	var level int = _level
	for {
		if level < 10 {
			break
		}
		taba = taba + "\t"
		tabb = tabb + "\t"
		level = level / 10
	}
	//
	return taba, tabb
}

func GetJsonResult(_base gjs.Result, _key string) gjs.Result {
	return gjs.Get(_base.String(), _key)
}

func GetJsonString(_base gjs.Result, _key string) string {
	return GetJsonResult(_base, _key).String()
}

func GetJsonInt(_base gjs.Result, _key string) int64 {
	return GetJsonResult(_base, _key).Int()
}

func GetObjectFromStr(_objstr string) gjs.Result {
	var resobj gjs.Result = gjs.Get(`{"data":`+_objstr+`}`, "data")
	//
	return resobj
}

func DoFilterByJsonStr(_objstr gjs.Result) gjs.Result {
	if _objstr.Type == gjs.String {
		_objstr = GetObjectFromStr(string(`"` + DoFilterByStr(_objstr.String()) + `"`))
	}
	//
	return _objstr
}

func DoFilterByStr(_str string) string {
	var svalue string = strings.ReplaceAll(_str, "\n", " ")
	if reflect.TypeOf(_str).Name() == "string" {
		if strings.Contains(_str, `"`) {
			evalarr := strings.Split(_str, `"`)
			if len(evalarr) > 0 {
				smarshall, err := json.Marshal(_str)
				if err != nil {
					svalue = "Wrong count of double quoted sign."
				} else {
					svalue = strings.Trim(string(smarshall), `"`)
				}
			} else {
				svalue = html.EscapeString(svalue)
				// svalue = strings.ReplaceAll(svalue, `"`, "&quot;")
			}
		}
	}
	svalue = strings.ReplaceAll(svalue, "\t", "&#9;")
	//
	return svalue
}

func JsonSearch(_json gjs.Result, _key, _value string, _level int64, _caption gjs.Result) (gjs.Result, bool) {
	if _level == 0 {
		_caption.Type = gjs.Null
	} else {
		if _caption.Type != gjs.Null {
			_caption = DoFilterByJsonStr(_caption)
			//
			return _caption, true
		}
	}
	var result bool = false
	if _json.IsObject() {
		var keysearch bool = (len(_key) > 0)
		_json.ForEach(func(jkey, jvalue gjs.Result) bool {
			if jvalue.IsArray() {
				_caption, result = JsonSearch(jvalue, _key, _value, _level+1, _caption)
				// fmt.Println(GetTabLevel(_level), "(oa)", jkey, "=", _caption)
			} else {
				if jvalue.IsObject() {
					_caption, result = JsonSearch(jvalue, _key, _value, _level+1, _caption)
					// fmt.Println(GetTabLevel(_level), "(oo)", jkey, "=", _caption)
				} else {
					// fmt.Println(GetTabLevel(_level), "(ov)", _key, _value, jkey, "=", jvalue)
					if keysearch {
						if jkey.String() == _key {
							if jvalue.String() == _value {
								_caption = gjs.Get(_json.String(), "value")
								result = (_caption.Type != gjs.Null)
								// fmt.Println(GetTabLevel(_level), "(< [", _level, "] ov >)", _key, ".", _value, "=", _caption, _value)
							}
						}
					} else {
						if jkey.String() == _value {
							_caption = jvalue
							result = (_caption.Type != gjs.Null)
							// fmt.Println(GetTabLevel(_level), "(< [", _level, "] ov >)", _key, ".", _value, "=", _caption, _value)
						}
					}
				}
			}
			if _caption.Type != gjs.Null {
				return false
			}
			return true
		})
	} else {
		if _json.IsArray() {
			for jkey, jvalue := range _json.Array() {
				jkey = jkey
				if jvalue.IsArray() {
					// log.Println(GetTabLevel(_level), "(aa)", jkey, "=")
					_caption, result = JsonSearch(jvalue, _key, _value, _level+1, _caption)
				} else {
					if jvalue.IsObject() {
						// log.Println(GetTabLevel(_level), "(ao)", jkey, "=")
						_caption, result = JsonSearch(jvalue, _key, _value, _level+1, _caption)
					} else {
						// log.Println(GetTabLevel(_level), "(av)", jkey, "=", jvalue)
						if jvalue.String() == _value {
							_caption = gjs.Get(_json.String(), "value")
							// log.Println(GetTabLevel(_level), "(< [", _level, "] av >)", _key, ".", _value, "=", _caption, _value)
							break
						}
					}
				}
			}
		}
	}
	if _caption.Type != gjs.Null {
		_caption = DoFilterByJsonStr(_caption)
	}
	//
	// fmt.Println(GetTabLevel(_level), "(res)", _key, _value, "=", _caption)
	return _caption, result
}

func AddValues(_col, _val string, _caption, _value gjs.Result) (string, string) {
	_col = AddStrings(_col, `"`+_caption.String(), `",`)
	_val = AddStrings(_val, `"`+_caption.String()+`":"`+DoFilterByStr(_value.String())+`",`, `,`)
	//
	return _col, _val
}

func AddColumnToMap(_col ColMap, _key, _value gjs.Result) ColMap {
	if len(_key.String()) > 0 {
		if _value.IsObject() {
			return _col
		}
		if len(_col) == 0 {
			_col = make(ColMap)
		}
		_value = DoFilterByJsonStr(_value)
		_col[`"`+_key.String()+`"`] = `"` + _value.String() + `"`
	}
	//
	return _col
}

func AddColumnToMapAsString(_col ColMap, _key, _value string) ColMap {
	if len(_key) > 0 {
		if len(_col) == 0 {
			_col = make(ColMap)
		}
		// _col[_key] = _value
		_value = DoFilterByStr(_value)
		_col[`"`+_key+`"`] = `"` + _value + `"`
	}
	//
	return _col
}

func AddValueToMap(_row RowMap, _col ColMap,
	_index, _key, _value gjs.Result, _exclude, _include string) (RowMap, ColMap) {
	if len(_key.String()) > 0 {
		if _value.IsObject() {
			// log.Println("object:", _key, ",", _value)
			_value.ForEach(func(fkey, fvalue gjs.Result) bool {
				if fkey.String() == "typeName" {
					var typevalue gjs.Result
					typevalue, _ = JsonSearch(_value, "", "value", 0, typevalue)
					// log.Println("type(1):", fvalue, ",", typevalue)
					if typevalue.IsObject() || typevalue.IsArray() {
						return true
					}
					_row, _col = AddValueToMap(_row, _col, _index, fvalue, typevalue, _exclude, _include)
					return false
				}
				// log.Println("type(2):", fkey, ",", fvalue)
				_row, _col = AddValueToMap(_row, _col, _index, fkey, fvalue, _exclude, _include)
				return true
			})
			return _row, _col
		} else {
			if _value.IsArray() {
				for _, avalue := range _value.Array() {
					// log.Println("array(3):", _key, ",", avalue)
					_row, _col = AddValueToMap(_row, _col, _index, _key, avalue, _exclude, _include)
				}
				return _row, _col
			} else {
				var typevalue gjs.Result = DoFilterByJsonStr(_value)
				// fmt.Println("AddValueToMap()", _key, ":", typevalue)
				if len(_row) == 0 {
					_row = make(RowMap)
				}
				if len(_row[`"`+_index.String()+`"`]) == 0 {
					_row[`"`+_index.String()+`"`] = make(ColMap)
				}
				_row[`"`+_index.String()+`"`][`"`+_key.String()+`"`] = `"` + typevalue.String() + `"`
				_col = AddColumnToMap(_col, _key, _key)
			}
		}
	}
	//
	return _row, _col
}

func JsonSearchSet(_json gjs.Result, _key, _val string, _level int64, _caption, _value gjs.Result) (gjs.Result, gjs.Result, bool) {
	if _level == 0 {
		_value.Type = gjs.Null
	} else {
		if _value.Type != gjs.Null {
			return _caption, _value, true
		}
	}

	var result bool = false
	if _json.IsObject() {
		_json.ForEach(func(jkey, jvalue gjs.Result) bool {
			// log.Println(GetTabLevel(_level), "(oo)", _key, _val, jkey, jvalue)
			if jvalue.IsArray() {
				// log.Println(GetTabLevel(_level), "(oa)", _key, _val, jkey, "=", jvalue, _caption, _value)
				_caption, _value, result = JsonSearchSet(jvalue, _key, _val, _level+1, _caption, _value)
			} else {
				if jvalue.IsObject() {
					var oocaption gjs.Result
					var oovalue gjs.Result
					_caption, _value, result = JsonSearchSet(jvalue, _key, _val, _level+1, oocaption, oovalue)
					if result {
						// log.Println(GetTabLevel(_level), "(ooo)", jkey, "=", jvalue, _caption, _value)
						icnt = icnt + 1
						Col, Val = AddValues(Col, Val, _caption, _value)
						return false
					}
					// log.Println(GetTabLevel(_level), "(oo)", jkey, "=", jvalue, _caption, _value)
				} else {
					// log.Println(GetTabLevel(_level), "(ov)", _key, _val, jkey, "=", jvalue)
					if jkey.String() == _key {
						_caption = gjs.Get(_json.String(), _key)
						return true
					}
					if jkey.String() == _val {
						// log.Println(GetTabLevel(_level), "(< [", _level, "] ov >)", _key, ".", _val, "=", _caption, ".", _value)
						_value = gjs.Get(_json.String(), _val)
						icnt = icnt + 1
						Col, Val = AddValues(Col, Val, _caption, _value)
						return false
					}
				}
			}
			// log.Println(GetTabLevel(_level), "(< [", _level, "] ov >)", _key, ".", _val, "=", _caption, ".", _value)
			if _value.Type != gjs.Null {
				return false
			}
			return true
		})
		if _value.Type != gjs.Null {
			// log.Println("JsonSearchSet(fo):", _level, _caption, _value)
			return _caption, _value, true
		}
	} else {
		if _json.IsArray() {
			for _, jvalue := range _json.Array() {
				// log.Println(GetTabLevel(_level), "(aa)", _key, _val, jkey, "=", jvalue, _caption, _value)
				if jvalue.IsArray() {
					// log.Println(GetTabLevel(_level), "(aa)", jkey, "=")
					_caption, _value, result = JsonSearchSet(jvalue, _key, _val, _level+1, _caption, _value)
					if result {
						// log.Println(GetTabLevel(_level), "(ooo)", jkey, "=", jvalue, _caption, _value)
						icnt = icnt + 1
						Col, Val = AddValues(Col, Val, _caption, _value)
					}
				} else {
					if jvalue.IsObject() {
						jvalue.ForEach(func(jjkey, jjvalue gjs.Result) bool {
							// log.Println(GetTabLevel(_level), "(ooo)", jjkey, jjvalue)
							if jjvalue.IsObject() {
								var oocaption gjs.Result
								var oovalue gjs.Result
								oocaption, oovalue, result = JsonSearchSet(jjvalue, _key, _val, _level+1, oocaption, oovalue)
								if result {
									if oovalue.Type != gjs.Null {
										icnt = icnt + 1
										Col, Val = AddValues(Col, Val, _caption, _value)
									}
								}
							}
							return true
						})
					} else {
						// log.Println(GetTabLevel(_level), "(av)", jkey, "=", jvalue)
						if jvalue.String() == _val {
							_value = gjs.Get(_json.String(), "value")
							icnt = icnt + 1
							Col, Val = AddValues(Col, Val, _caption, _value)
						}
					}
				}
				if _value.Type != gjs.Null {
					// log.Println("JsonSearchSet(fa):", _level, _caption, _value)
					return _caption, _value, true
				}
			}
		}
	}
	// log.Println("JsonSearchSet(end):", _level, _caption, _value)
	return _caption, _value, result
}

func CreateLogin(r *http.Request) string {
	_prefix := r.FormValue("pref")
	_middle := r.FormValue("midl")
	_suffix := r.FormValue("suff")
	//
	var html []string
	html = append(html, "<div id=\""+_prefix+_middle+_suffix+"\" class = \"easyui-panel\" style=\"width:400px;\" data-options=\"")
	// panel
	html = append(html, " footer:'#"+_prefix+_middle+_suffix+"_footer'")
	// window
	html = append(html, ",modal:true")
	html = append(html, ",resizable:true")
	html = append(html, ",minimizable:false")
	html = append(html, ",maximizable:false")
	html = append(html, ",collapsible:false")
	html = append(html, ",cache:false")
	html = append(html, ",draggable:true")
	html = append(html, ",title:''")
	html = append(html, "\">")
	html = append(html, "<div style=\"height:15px;\">&nbsp;</div>")
	html = append(html, "<table cellpadding=\"5\" style=\"width:100%;\">")
	html = append(html, "<tbody>")
	html = append(html, "<tr>")
	html = append(html, "<td text=\"L_logon_user\" width=\"30%\" style=\"margin-top:10px;\">Anmeldename:</td>")
	html = append(html, "<td style=\"margin-top:10px;\">")
	html = append(html, "<input class=\"easyui-textbox\" id=\"login_frm_user\" name=\"login_frm_user\" value=\"\" autocomplete=\"off\" data-options=\"iconCls:'icon-lock'\" style=\"width:95%\">")
	html = append(html, "</tr>")
	html = append(html, "<tr>")
	html = append(html, "<td text=\"L_logon_pwd\" width=\"30%\">Passwort:</td>")
	html = append(html, "<td>")
	html = append(html, "<input class=\"easyui-textbox\" type=\"password\" id=\"login_frm_pwd\" name=\"login_frm_pwd\" value=\"\" autocomplete=\"off\" data-options=\"iconCls:'icon-lock'\" style=\"width:95%\">")
	html = append(html, "</td>")
	html = append(html, "</tr>")
	html = append(html, "<tr>")
	html = append(html, "<td colspan=\"1\" style=\"text-align:left; padding-top:10px;\" width=\"\">")
	html = append(html, "<a>&nbsp;</a>")
	html = append(html, "</td>")
	html = append(html, "<td colspan=\"1\" align=\"left\" style=\"padding-top:10px;padding-bottom:10px;\">")
	html = append(html, "<a text=\"AC_host_logon\" id=\"btn_login_frm\" href=\"#\" class=\"easyui-linkbutton\" onclick=\"setLogin();\" style=\"padding:4px 40px 4px 40px;\" group=\"\">")
	html = append(html, "Anmelden</a>")
	html = append(html, "</td>")
	html = append(html, "</tr>")
	html = append(html, "</tbody>")
	html = append(html, "</table>")
	html = append(html, "</div>")
	html = append(html, "<div id=\""+_prefix+_middle+_suffix+"_footer\" style=\"padding:3px; font-size:75%; text-align:right;\">")
	html = append(html, "<div id=\""+_prefix+_middle+_suffix+"_copyright\" style=\"padding:2px; font-size:75%; text-align:center;\">Copyright &copy; 2010 <a href=\"http://www.archium.org\" target=\"_blank\">archium GmbH</a></div>")
	html = append(html, "</div>")
	//
	// log.Println("HTML:", strings.Join(html, ""))
	return strings.Join(html, "")
}
