// +build ignore

// Copyright 2014 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
)

type t int

const (
	qNil t = iota
	idealComplex
	idealFloat
	idealInt
	idealRune
	idealUint
	qBool
	qComplex64
	qComplex128
	qFloat32
	qFloat64
	qInt8
	qInt16
	qInt32
	qInt64
	qString
	qUint8
	qUint16
	qUint32
	qUint64
	qBigInt
	qBigRat
	qTime
	qDuration

	qEnd
)

func (n t) String() string {
	switch n {
	case qNil:
		return "nil"
	case idealComplex:
		return "idealComplex"
	case idealFloat:
		return "idealFloat"
	case idealInt:
		return "idealInt"
	case idealRune:
		return "idealRune"
	case idealUint:
		return "idealUint"
	case qBool:
		return "bool"
	case qComplex64:
		return "complex64"
	case qComplex128:
		return "complex128"
	case qFloat32:
		return "float32"
	case qFloat64:
		return "float64"
	case qInt8:
		return "int8"
	case qInt16:
		return "int16"
	case qInt32:
		return "int32"
	case qInt64:
		return "int64"
	case qString:
		return "string"
	case qUint8:
		return "uint8"
	case qUint16:
		return "uint16"
	case qUint32:
		return "uint32"
	case qUint64:
		return "uint64"
	case qBigInt:
		return "*big.Int"
	case qBigRat:
		return "*big.Rat"
	case qTime:
		return "time.Time"
	case qDuration:
		return "time.Duration"
	default:
		panic("internal error 046")
	}
}

func coerceIdealComplex(typ t) string {
	switch typ {
	case qComplex64, qComplex128:
		return fmt.Sprintf("return %s(x)\n", typ)
	default:
		return ""
	}
}

func coerceIdealFloat(typ t) string {
	switch typ {
	case idealComplex:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case qComplex64:
		return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ)
	case qComplex128:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case idealFloat, qFloat32, qFloat64:
		return fmt.Sprintf("return %s(float64(x))\n", typ)
	case qBigRat:
		return fmt.Sprintf("return big.NewRat(1, 1).SetFloat64(float64(x))\n")
	default:
		return ""
	}
	return ""
}

func coerceIdealInt(typ t) string {
	switch typ {
	case idealComplex:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case qComplex64:
		return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ)
	case qComplex128:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case idealFloat, idealInt, qFloat32, qFloat64, qInt64:
		return fmt.Sprintf("return %s(int64(x))\n", typ)
	case idealUint:
		return fmt.Sprintf("if x >= 0 { return %s(int64(x)) }\n", typ)
	case qInt8:
		return fmt.Sprintf("if x >= math.MinInt8 && x<= math.MaxInt8 { return %s(int64(x)) }\n", typ)
	case qInt16:
		return fmt.Sprintf("if x >= math.MinInt16 && x<= math.MaxInt16 { return %s(int64(x)) }\n", typ)
	case qInt32:
		return fmt.Sprintf("if x >= math.MinInt32 && x<= math.MaxInt32 { return %s(int64(x)) }\n", typ)
	case qUint8:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint8 { return %s(int64(x)) }\n", typ)
	case qUint16:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint16 { return %s(int64(x)) }\n", typ)
	case qUint32:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint32 { return %s(int64(x)) }\n", typ)
	case qUint64:
		return fmt.Sprintf("if x >= 0 { return %s(int64(x)) }\n", typ)
	case qBigInt:
		return fmt.Sprintf("return big.NewInt(int64(x))\n")
	case qBigRat:
		return fmt.Sprintf("return big.NewRat(1, 1).SetInt64(int64(x))\n")
	case qDuration:
		return fmt.Sprintf("return time.Duration(int64(x))\n")
	default:
		return ""
	}
	return ""
}

func coerceIdealRune(typ t) string {
	switch typ {
	case idealComplex:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case qComplex64:
		return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ)
	case qComplex128:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case idealFloat, idealInt, idealRune, idealUint, qFloat32, qFloat64, qInt8, qInt16, qInt32, qInt64, qUint8, qUint16, qUint32, qUint64:
		return fmt.Sprintf("return %s(int64(x))\n", typ)
	case qBigInt:
		return fmt.Sprintf("return big.NewInt(int64(x))\n")
	case qBigRat:
		return fmt.Sprintf("return big.NewRat(1, 1).SetInt64(int64(x))\n")
	case qDuration:
		return fmt.Sprintf("return time.Duration(int64(x))\n")
	default:
		return ""
	}
	return ""
}

func coerceIdealUint(typ t) string {
	switch typ {
	case idealComplex:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case qComplex64:
		return fmt.Sprintf("return %s(complex(float32(x), 0))\n", typ)
	case qComplex128:
		return fmt.Sprintf("return %s(complex(float64(x), 0))\n", typ)
	case idealFloat, idealUint, qFloat32, qFloat64, qUint64:
		return fmt.Sprintf("return %s(uint64(x))\n", typ)
	case idealInt:
		return fmt.Sprintf("if x <= math.MaxInt64 { return %s(int64(x)) }\n", typ)
	case qInt8:
		return fmt.Sprintf("if x <= math.MaxInt8 { return %s(int64(x)) }\n", typ)
	case qInt16:
		return fmt.Sprintf("if  x<= math.MaxInt16 { return %s(int64(x)) }\n", typ)
	case qInt32:
		return fmt.Sprintf("if  x<= math.MaxInt32 { return %s(int64(x)) }\n", typ)
	case qInt64:
		return fmt.Sprintf("if  x<= math.MaxInt64 { return %s(int64(x)) }\n", typ)
	case qUint8:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint8 { return %s(int64(x)) }\n", typ)
	case qUint16:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint16 { return %s(int64(x)) }\n", typ)
	case qUint32:
		return fmt.Sprintf("if x >= 0 && x<= math.MaxUint32 { return %s(int64(x)) }\n", typ)
	case qBigInt:
		return fmt.Sprintf("return big.NewInt(0).SetUint64(uint64(x))\n")
	case qBigRat:
		return fmt.Sprintf("return big.NewRat(1, 1).SetInt(big.NewInt(0).SetUint64(uint64(x)))\n")
	case qDuration:
		return fmt.Sprintf("if x <= math.MaxInt64 { return time.Duration(int64(x)) }\n")
	default:
		return ""
	}
	return ""
}

func genCoerce1(w io.Writer, in t, f func(out t) string) {
	fmt.Fprintf(w, "\tcase %s:\n", in)
	fmt.Fprintf(w, "\t\tswitch otherVal.(type) {\n")

	for i := idealComplex; i < qEnd; i++ {
		s := f(i)
		switch s {
		case "":
			fmt.Fprintf(w, "\t\t//case %s:\n", i)
		default:
			fmt.Fprintf(w, "\t\tcase %s:\n", i)
			fmt.Fprintf(w, "\t\t\t%s", s)
		}
	}

	fmt.Fprintf(w, "\t\t}\n") // switch
}

func genCoerce(w io.Writer) {
	fmt.Fprintf(w,
		`
func coerce1(inVal, otherVal interface{}) (coercedInVal interface{}) {
	coercedInVal = inVal
	if otherVal == nil {
		return
	}

	switch x := inVal.(type) {
	case nil:
		return
`)
	genCoerce1(w, idealComplex, coerceIdealComplex)
	genCoerce1(w, idealFloat, coerceIdealFloat)
	genCoerce1(w, idealInt, coerceIdealInt)
	genCoerce1(w, idealRune, coerceIdealRune)
	genCoerce1(w, idealUint, coerceIdealUint)
	fmt.Fprintf(w, "\t}\n") // switch

	fmt.Fprintf(w, "\treturn\n}\n") // func
}

func main() {
	ofn := flag.String("o", "", "")
	flag.Parse()
	_, err := os.Stat(*ofn)
	if err == nil {
		log.Fatalf("%s exists", *ofn)
	}

	w := bufio.NewWriter(os.Stdout)
	if s := *ofn; s != "" {
		f, err := os.Create(s)
		if err != nil {
			log.Fatal(err)
		}

		defer f.Close()
		w = bufio.NewWriter(f)
	}
	defer w.Flush()

	fmt.Fprintf(w, `// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// CAUTION: This file was generated automatically by
//
//	$ go run helper/helper.go -o coerce.go
//
// DO NOT EDIT!

 package ql

import (
	"math"
	"math/big"
	"reflect"
	"time"
)

func coerce(a, b interface{}) (x, y interface{}) {
	if reflect.TypeOf(a) == reflect.TypeOf(b) {
		return a, b
	}

	switch a.(type) {
	case idealComplex, idealFloat, idealInt, idealRune, idealUint:
		switch b.(type) {
		case idealComplex, idealFloat, idealInt, idealRune, idealUint:
			x, y = coerce1(a, b), b
			if reflect.TypeOf(x) == reflect.TypeOf(y) {
				return
			}

			return a, coerce1(b, a)
		default:
			return coerce1(a, b), b
		}
	default:
		switch b.(type) {
		case idealComplex, idealFloat, idealInt, idealRune, idealUint:
			return a, coerce1(b, a)
		default:
			return a, b
		}
	}
}
`)
	genCoerce(w)
}
