package gouda

import (
	"container/vector"
	"fmt"
	"reflect"
	"strings"
)

type RequestKind int
type OperandKind int

type Condition struct {
	field   string
	operand OperandKind
	value   Value
	or      vector.Vector
}

const (
	NULL OperandKind = iota
	EQUAL
	NOTEQUAL
	ISNOTNULL
	ISNULL
	GREATER
	LOWER
	GREATEROREQUAL
	LOWEROREQUAL
	OR
)
const (
	SELECT RequestKind = iota
	UPDATE
	INSERT
	COUNT
	DELETE
)

type Relation struct {
	conditions       vector.Vector
	table            string
	limit_offset     int
	limit_count      int
	order_field      vector.StringVector
	order_direction  vector.StringVector
	kind             RequestKind
	attributes       vector.StringVector
	values           map[string]Value
	id               int
	identifier_field string
}

func (r *Relation) Count(fields []string) *Relation {
	for _, s := range fields {
		r.attributes.Push(s)
	}
	r.kind = COUNT
	return r
}

func (r *Relation) Delete() *Relation {
	r.kind = DELETE
	return r
}

func (r *Relation) Insert(mp map[string]Value) *Relation {
	r.kind = INSERT
	r.values = mp
	return r
}

func (r *Relation) Update(mp map[string]Value, identifier string, id int) *Relation {
	r.kind = UPDATE
	r.values = mp
	r.id = id
	r.identifier_field = identifier
	return r
}

func (r *Relation) Where(s *Condition) *Relation {

	r.conditions.Push(s)

	return r

}

func (r *Relation) Table(t string) *Relation {
	r.table = t
	return r
}

func (r *Relation) Limit(offset, count int) *Relation {
	r.limit_offset = offset
	r.limit_count = count
	return r
}

func (r *Relation) First() *Relation {
	r.Limit(0, 1)
	return r
}

func (r *Relation) Order(field, direction string) *Relation {
	r.order_field.Push(field)
	r.order_direction.Push(direction)
	return r
}

func (r *Relation) String() string {

	s := " Conditions : \n\t ("

	for _, ss := range r.conditions {
		s += ss.(*Condition).String()
		if ss != r.conditions.Last() {
			s += ", "
		}
	}
	s += ") \n"
	if r.order_field.Len() > 0 {
		s += "Order :\n\t ("
		for i, ss := range r.order_field {
			s += ss
			s += " "
			s += r.order_direction[i]
			if ss != r.order_field.Last() {
				s += ", "
			}
		}
		s += ") \n"
	}
	if r.limit_count > 0 {
		s += "Offset : " + fmt.Sprint(r.limit_offset) + "\n"
		s += "Count : " + fmt.Sprint(r.limit_count) + "\n"
	}
	return "Une relation" + s
}

func From(t interface{}) (r *Relation) { return NewRelation(t) }

func NewRelation(t interface{}) (r *Relation) {
	r = new(Relation)
	r.kind = SELECT
	switch typ := t.(type) {
	case string:
		r.Table(t.(string))
	default:
		tab := strings.Split(reflect.Typeof(t).String(), ".", 0)
		tablename := strings.ToLower(tab[len(tab)-1])
		r.Table(tablename)
	}
	return
}


/** Condition **/

func F(field string) (c *Condition) {
	c = new(Condition)
	c.operand = NULL
	c.field = strings.ToLower(field)
	return
}

func (c *Condition) Value(v interface{}) *Condition {
	switch v.(type) {
	case int:
		c.value = SysInt(v.(int)).Value()
	case string:
		c.value = SysString(v.(string)).Value()
	}
	return c
}

func (c *Condition) Eq(v interface{}) *Condition {
	c.operand = EQUAL
	return c.Value(v)
}

func (c *Condition) NEq(v interface{}) *Condition {
	c.operand = NOTEQUAL
	return c.Value(v)
}

func (c *Condition) Gt(v interface{}) *Condition {
	c.operand = GREATER
	return c.Value(v)
}

func (c *Condition) Lt(v interface{}) *Condition {
	c.operand = LOWER
	return c.Value(v)
}

func (c *Condition) GtEq(v interface{}) *Condition {
	c.operand = GREATEROREQUAL
	return c.Value(v)
}

func (c *Condition) LtEq(v interface{}) *Condition {
	c.operand = LOWEROREQUAL
	return c.Value(v)
}


func (c *Condition) IsNotNull() *Condition {
	c.operand = ISNOTNULL
	return c
}

func (c *Condition) IsNull() *Condition {
	c.operand = ISNULL
	return c
}

func (c *Condition) Or(c2 *Condition) *Condition {
	if c.operand != OR {
		orc := new(Condition)
		orc.operand = OR
		orc.or.Push(c)
		c = orc
	}
	c.or.Push(c2)
	return c
}

func (c *Condition) String() string {
	if c.operand == OR {
		ret := " ( "
		for i := range c.or {
			ret += c.or.At(i).(*Condition).String()
			if i != c.or.Len()-1 {
				ret += " ) OR ("
			}
		}
		ret += ")"
		return ret
	}
	ret := c.field
	switch c.operand {
	case EQUAL:
		ret += " = "
	case NOTEQUAL:
		ret += " != "
	case ISNOTNULL:
		ret += " IS NOT NULL "
	case ISNULL:
		ret += " IS NULL "
	case LOWER:
		ret += " < "
	case GREATER:
		ret += " > "
	case LOWEROREQUAL:
		ret += " <= "
	case GREATEROREQUAL:
		ret += " >= "
	default:
		ret += " NO SE OP"
	}
	if c.operand != ISNOTNULL && c.operand != ISNULL {
		switch c.value.Kind() {
		case IntKind:
			ret += fmt.Sprint(int(c.value.Int()))
		case StringKind:
			ret += fmt.Sprint(string(c.value.String()))
		}
	}
	return ret
}