package ical

import (
	"errors"
	"log"
	"regexp"
	"strings"
)

func ParseCalendar(data string) (*Node, error) {
	r := regexp.MustCompile("([\r|\t| ]*\n[\r|\t| ]*)+")
	lines := r.Split(strings.TrimSpace(data), -1)
	node, _, err, _ := parseCalendarNode(lines, 0)

	return node, err
}

func parseCalendarNode(lines []string, lineIndex int) (*Node, bool, error, int) {
	line := strings.TrimSpace(lines[lineIndex])
	_ = log.Println
	colonIndex := strings.Index(line, ":")
	if colonIndex <= 0 {
		return nil, false, errors.New("Invalid value/pair: " + line), lineIndex + 1
	}
	name := line[0:colonIndex]
	splitted := strings.Split(name, ";")
	var parameters map[string]string
	if len(splitted) >= 2 {
		name = splitted[0]
		parameters = make(map[string]string)
		for i := 1; i < len(splitted); i++ {
			p := strings.Split(splitted[i], "=")
			if len(p) != 2 {
				panic("Invalid parameter format: " + name)
			}
			parameters[p[0]] = p[1]
		}
	}
	value := line[colonIndex+1 : len(line)]

	if name == "BEGIN" {
		node := new(Node)
		node.Name = value
		node.Type = 1
		lineIndex = lineIndex + 1
		for {
			child, finished, _, newLineIndex := parseCalendarNode(lines, lineIndex)
			if finished {
				return node, false, nil, newLineIndex
			} else {
				if child != nil {
					node.Children = append(node.Children, child)
				}
				lineIndex = newLineIndex
			}
		}
	} else if name == "END" {
		return nil, true, nil, lineIndex + 1
	} else {
		node := new(Node)
		node.Name = name
		if name == "DESCRIPTION" || name == "SUMMARY" {
			text, newLineIndex := parseTextType(lines, lineIndex)
			node.Value = text
			node.Parameters = parameters
			return node, false, nil, newLineIndex
		} else {
			node.Value = value
			node.Parameters = parameters
			return node, false, nil, lineIndex + 1
		}
	}

	panic("Unreachable")
	return nil, false, nil, lineIndex + 1
}

func parseTextType(lines []string, lineIndex int) (string, int) {
	line := lines[lineIndex]
	colonIndex := strings.Index(line, ":")
	output := strings.TrimSpace(line[colonIndex+1 : len(line)])
	lineIndex++
	for {
		line := lines[lineIndex]
		if line == "" || line[0] != ' ' {
			return unescapeTextType(output), lineIndex
		}
		output += line[1:len(line)]
		lineIndex++
	}
	return unescapeTextType(output), lineIndex
}

func escapeTextType(input string) string {
	output := strings.Replace(input, "\\", "\\\\", -1)
	output = strings.Replace(output, ";", "\\;", -1)
	output = strings.Replace(output, ",", "\\,", -1)
	output = strings.Replace(output, "\n", "\\n", -1)
	return output
}

func unescapeTextType(s string) string {
	s = strings.Replace(s, "\\;", ";", -1)
	s = strings.Replace(s, "\\,", ",", -1)
	s = strings.Replace(s, "\\n", "\n", -1)
	s = strings.Replace(s, "\\\\", "\\", -1)
	return s
}