package main

import (
	"errors"
	"flag"
	"image"
	"image/color"
	"log"

	"github.com/llgcode/draw2d/draw2dkit"

	"github.com/llgcode/draw2d/draw2dimg"
)

func main() {
	err := run()
	if err != nil {
		log.Fatal(err)
	}
}

type projection interface {
	tile() (width float64, height float64)
	draw(gc *draw2dimg.GraphicContext, x, y, w, h, scale float64, p palette)
}

type cabinet struct {
}

func (cabinet) tile() (float64, float64) {
	return 150, 140
}

func (cabinet) draw(gc *draw2dimg.GraphicContext, x, y, w, h, scale float64, p palette) {
	dw := func(d float64) float64 { return (d / w) * w * scale }
	dh := func(d float64) float64 { return (d / h) * h * scale }
	gc.MoveTo(x, y+dh(40))
	gc.LineTo(x, y+h)
	gc.LineTo(x+dw(100), y+h)
	gc.LineTo(x+w, y+dh(100))
	gc.LineTo(x+w, y)
	gc.LineTo(x+dw(100), y+dh(40))
	gc.LineTo(x+dw(100), y+h)
	gc.MoveTo(x+dw(100), y+dh(40))
	gc.LineTo(x, y+dh(40))
	gc.LineTo(x+dw(50), y)
	gc.LineTo(x+w, y)
	gc.LineTo(x+dw(100), y+dh(40))
	gc.SetStrokeColor(p.tile)
	gc.Stroke()
}

type isometric struct {
}

func (isometric) tile() (float64, float64) {
	return 140, 200
}

type palette struct {
	margin color.Color
	tile   color.Color
}

func (isometric) draw(gc *draw2dimg.GraphicContext, x, y, w, h, scale float64, p palette) {
	gc.MoveTo(x, y+.25*h)
	gc.LineTo(x, y+.75*h)
	gc.LineTo(x+.5*w, y+h)
	gc.LineTo(x+w, y+.75*h)
	gc.LineTo(x+w, y+.25*h)
	gc.LineTo(x+.5*w, y+.5*h)
	gc.LineTo(x, y+.25*h)
	gc.LineTo(x+.5*w, y)
	gc.LineTo(x+w, y+.25*h)
	gc.MoveTo(x+.5*w, y+.5*h)
	gc.LineTo(x+.5*w, y+h)
	gc.SetStrokeColor(p.tile)
	gc.Stroke()
}

func run() error {
	var dpi int
	var proj string
	flag.IntVar(&dpi, "dpi", 300, "dpi")
	flag.StringVar(&proj, "projection", "", "projection: either isometric (default) or cabinet")
	flag.Parse()
	path := flag.Arg(0)
	if path == "" {
		return errors.New("no output path specified")
	}

	var p projection
	switch proj {
	case "cabinet":
		p = cabinet{}
	default:
		p = isometric{}
	}

	bounds := image.Rect(0, 0, dpi*827/100, dpi*1169/100)
	scale := float64(dpi) / 100
	margin := 5. * scale
	tileWidth, tileHeight := p.tile()
	tileWidth, tileHeight = tileWidth*scale, tileHeight*scale
	tileOffsetWidth, tileOffsetHeight := tileWidth+4.*margin, tileHeight+4.*margin
	im := image.NewRGBA(bounds)
	gc := draw2dimg.NewGraphicContext(im)
	gc.SetLineWidth(1)
	palette := palette{margin: color.Gray{Y: 63}, tile: color.Gray{Y: 127}}
	for y := 0.; y < float64(bounds.Max.Y)-tileOffsetHeight; y += tileOffsetHeight {
		for x := 0.; x < float64(bounds.Max.X)-tileOffsetWidth; x += tileOffsetWidth {
			draw2dkit.Rectangle(gc, x+margin-1, y+margin-1, x+tileOffsetWidth-margin, y+tileOffsetHeight-margin)
			gc.SetStrokeColor(palette.margin)
			gc.Stroke()

			tileX, tileY := x+2*margin-.5, y+2*margin-.5
			p.draw(gc, tileX, tileY, tileWidth, tileHeight, scale, palette)
		}
	}
	return draw2dimg.SaveToPngFile(path, im)
}
