// Copyright 2011 The Go 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 (
	"fmt"
	"syscall"
	"os"
	"unsafe"
	"image"
	"io/ioutil"
	"strings"
	"time"
	"draw2d.googlecode.com/hg/draw2d"
	"draw2d.googlecode.com/hg/postscript"
	"draw2d.googlecode.com/hg/wingui"
)

// some help functions

func abortf(format string, a ...interface{}) {
	fmt.Fprintf(os.Stdout, format, a...)
	os.Exit(1)
}

func abortErrNo(funcname string, err int) {
	abortf("%s failed: %d %s\n", funcname, err, syscall.Errstr(err))
}

// global vars


func TestDrawCubicCurve(gc draw2d.GraphicContext) {
	// draw a cubic curve
	x, y := 25.6, 128.0
	x1, y1 := 102.4, 230.4
	x2, y2 := 153.6, 25.6
	x3, y3 := 230.4, 128.0

	gc.SetFillColor(image.NRGBAColor{0xAA, 0xAA, 0xAA, 0xFF})
	gc.SetLineWidth(10)
	gc.MoveTo(x, y)
	gc.CubicCurveTo(x1, y1, x2, y2, x3, y3)
	gc.Stroke()

	gc.SetStrokeColor(image.NRGBAColor{0xFF, 0x33, 0x33, 0x88})

	gc.SetLineWidth(6)
	// draw segment of curve
	gc.MoveTo(x, y)
	gc.LineTo(x1, y1)
	gc.LineTo(x2, y2)
	gc.LineTo(x3, y3)
	gc.Stroke()
}

var (
	mh                uint32
	wndBufferHeader   uint32
	wndBuffer         wingui.BITMAP
	hdcWndBuffer      uint32
	ppvBits           *image.RGBAColor
	backBuffer        *image.RGBA
	postscriptContent string
)

// WinProc called by windows to notify us of all windows events we might be interested in.
func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr {
	var rc int32

	switch msg {
	case wingui.WM_CREATE:
		hdc := wingui.GetDC(hwnd)
		hdcWndBuffer = wingui.CreateCompatibleDC(hdc)
		wndBufferHeader = wingui.CreateCompatibleBitmap(hdc, 600, 800)
		wingui.GetObject(wndBufferHeader, unsafe.Sizeof(wndBuffer), uintptr(unsafe.Pointer(&wndBuffer)))
		wingui.SelectObject(hdcWndBuffer, wndBufferHeader)

		var bmp_header wingui.BITMAPINFOHEADER
		bmp_header.Size = uint32(unsafe.Sizeof(bmp_header))
		bmp_header.Width = 600
		bmp_header.Height = 800
		bmp_header.SizeImage = 0 // the api says this must be 0 for BI_RGB images
		bmp_header.Compression = wingui.BI_RGB
		bmp_header.BitCount = 32
		bmp_header.Planes = 1
		bmp_header.XPelsPerMeter = 0
		bmp_header.YPelsPerMeter = 0
		bmp_header.ClrUsed = 0
		bmp_header.ClrImportant = 0
		//bitmap info
		var bmpinfo wingui.BITMAPINFO
		bmpinfo.Colors[0].Blue = 0
		bmpinfo.Colors[0].Green = 0
		bmpinfo.Colors[0].Red = 0
		bmpinfo.Colors[0].Reserved = 0
		bmpinfo.Header = bmp_header
		wndBufferHeader = wingui.CreateDIBSection(hdc, &bmpinfo, wingui.DIB_RGB_COLORS, uintptr(unsafe.Pointer(&ppvBits)), 0, 0)
		wingui.GetObject(wndBufferHeader, unsafe.Sizeof(wndBufferHeader), uintptr(unsafe.Pointer(&wndBuffer)))
		hdcWndBuffer = wingui.CreateCompatibleDC(hdc)
		wingui.SelectObject(hdcWndBuffer, wndBufferHeader)

		pixel := (*[600 * 800]image.RGBAColor)(unsafe.Pointer(ppvBits))
		pixelSlice := pixel[:]
		backBuffer = &image.RGBA{pixelSlice, 600, image.Rect(0, 0, 600, 800)}
		fmt.Println("Create windows")
		rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam)
	case wingui.WM_COMMAND:
		switch uint32(lparam) {
		default:
			rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam)
		}
	case wingui.WM_PAINT:
		var ps wingui.PAINTSTRUCT
		lastTime := time.Nanoseconds()
		hdc := wingui.BeginPaint(hwnd, &ps)
		gc := draw2d.NewGraphicContext(backBuffer)
		gc.SetFillColor(image.RGBAColor{0xFF, 0xFF, 0xFF, 0xFF})
		//	gc.Clear()
		gc.Save()
		//gc.Translate(0, -380)
		interpreter := postscript.NewInterpreter(gc)
		reader := strings.NewReader(postscriptContent)
		interpreter.Execute(reader)
		dt := time.Nanoseconds() - lastTime
		gc.Restore()
		// back buf in

		wingui.BitBlt(hdc, 0, 0, int(wndBuffer.Width), int(wndBuffer.Height), hdcWndBuffer, 0, 0, wingui.SRCCOPY)
		wingui.EndPaint(hwnd, &ps)
		rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam)
		fmt.Printf("Redraw in : %f ms\n", float64(dt)*1e-6)
	case wingui.WM_CLOSE:
		wingui.DestroyWindow(hwnd)
	case wingui.WM_DESTROY:
		wingui.PostQuitMessage(0)
	default:
		rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam)
	}
	return uintptr(rc)
}

func rungui() int {
	var e int

	// GetModuleHandle
	mh, e = wingui.GetModuleHandle(nil)
	if e != 0 {
		abortErrNo("GetModuleHandle", e)
	}

	// Get icon we're going to use.
	myicon, e := wingui.LoadIcon(0, wingui.IDI_APPLICATION)
	if e != 0 {
		abortErrNo("LoadIcon", e)
	}

	// Get cursor we're going to use.
	mycursor, e := wingui.LoadCursor(0, wingui.IDC_ARROW)
	if e != 0 {
		abortErrNo("LoadCursor", e)
	}

	// Create callback
	wproc := syscall.NewCallback(WndProc)

	// RegisterClassEx
	wcname := syscall.StringToUTF16Ptr("Test Draw2d")
	var wc wingui.Wndclassex
	wc.Size = uint32(unsafe.Sizeof(wc))
	wc.WndProc = wproc
	//wc.Style = wingui.CS_HREDRAW | wingui.CS_VREDRAW
	wc.Instance = mh
	wc.Icon = myicon
	wc.Cursor = mycursor
	wc.Background = 0
	wc.MenuName = nil
	wc.ClassName = wcname
	wc.IconSm = myicon
	if _, e := wingui.RegisterClassEx(&wc); e != 0 {
		abortErrNo("RegisterClassEx", e)
	}

	// CreateWindowEx
	wh, e := wingui.CreateWindowEx(
		wingui.WS_EX_CLIENTEDGE,
		wcname,
		syscall.StringToUTF16Ptr("My window"),
		wingui.WS_OVERLAPPEDWINDOW,
		wingui.CW_USEDEFAULT, wingui.CW_USEDEFAULT, 600, 800,
		0, 0, mh, 0)
	if e != 0 {
		abortErrNo("CreateWindowEx", e)
	}
	fmt.Printf("main window handle is %x\n", wh)

	// ShowWindow
	wingui.ShowWindow(wh, wingui.SW_SHOWDEFAULT)

	// UpdateWindow
	if e := wingui.UpdateWindow(wh); e != 0 {
		abortErrNo("UpdateWindow", e)
	}

	// Process all windows messages until WM_QUIT.
	var m wingui.Msg
	for {
		r, e := wingui.GetMessage(&m, 0, 0, 0)
		if e != 0 {
			abortErrNo("GetMessage", e)
		}
		if r == 0 {
			// WM_QUIT received -> get out
			break
		}
		wingui.TranslateMessage(&m)
		wingui.DispatchMessage(&m)
	}
	return int(m.Wparam)
}

func main() {
	src, err := os.OpenFile("../resource/postscript/tiger.ps", 0, 0)
	if err != nil {
		fmt.Println("can't find postscript file.")
		return
	}
	defer src.Close()
	bytes, err := ioutil.ReadAll(src)
	postscriptContent = string(bytes)
	rc := rungui()
	os.Exit(rc)
}
