package pdu

import (
	"bytes"
	"errors"
	"fmt"
	"io"

	"ShotRDP/grdp/core"
	"ShotRDP/grdp/glog"
)

type ControlFlag uint8

const (
	TS_STANDARD             = 0x01
	TS_SECONDARY            = 0x02
	TS_BOUNDS               = 0x04
	TS_TYPE_CHANGE          = 0x08
	TS_DELTA_COORDINATES    = 0x10
	TS_ZERO_BOUNDS_DELTAS   = 0x20
	TS_ZERO_FIELD_BYTE_BIT0 = 0x40
	TS_ZERO_FIELD_BYTE_BIT1 = 0x80
)

type PrimaryOrderType uint8

const (
	ORDER_TYPE_DSTBLT = 0x00 //0
	ORDER_TYPE_PATBLT = 0x01 //1
	ORDER_TYPE_SCRBLT = 0x02 //2
	//ORDER_TYPE_DRAWNINEGRID       = 0x07 //7
	//ORDER_TYPE_MULTI_DRAWNINEGRID = 0x08 //8
	ORDER_TYPE_LINETO     = 0x09 //9
	ORDER_TYPE_OPAQUERECT = 0x0A //10
	ORDER_TYPE_SAVEBITMAP = 0x0B //11
	ORDER_TYPE_MEMBLT     = 0x0D //13
	ORDER_TYPE_MEM3BLT    = 0x0E //14
	//ORDER_TYPE_MULTIDSTBLT        = 0x0F //15
	//ORDER_TYPE_MULTIPATBLT        = 0x10 //16
	//ORDER_TYPE_MULTISCRBLT        = 0x11 //17
	//ORDER_TYPE_MULTIOPAQUERECT    = 0x12 //18
	//ORDER_TYPE_FAST_INDEX         = 0x13 //19
	ORDER_TYPE_POLYGON_SC = 0x14 //20
	ORDER_TYPE_POLYGON_CB = 0x15 //21
	ORDER_TYPE_POLYLINE   = 0x16 //22
	//ORDER_TYPE_FAST_GLYPH         = 0x18 //24
	ORDER_TYPE_ELLIPSE_SC = 0x19 //25
	ORDER_TYPE_ELLIPSE_CB = 0x1A //26
	ORDER_TYPE_TEXT2      = 0x1B //27
)

type SecondaryOrderType uint8

const (
	ORDER_TYPE_BITMAP_UNCOMPRESSED     = 0x00
	ORDER_TYPE_CACHE_COLOR_TABLE       = 0x01
	ORDER_TYPE_CACHE_BITMAP_COMPRESSED = 0x02
	ORDER_TYPE_CACHE_GLYPH             = 0x03
	ORDER_TYPE_BITMAP_UNCOMPRESSED_V2  = 0x04
	ORDER_TYPE_BITMAP_COMPRESSED_V2    = 0x05
	ORDER_TYPE_CACHE_BRUSH             = 0x07
	ORDER_TYPE_BITMAP_COMPRESSED_V3    = 0x08
)

func (s SecondaryOrderType) String() string {
	name := "Unknown"
	switch s {
	case ORDER_TYPE_BITMAP_UNCOMPRESSED:
		name = "Cache Bitmap"
	case ORDER_TYPE_CACHE_COLOR_TABLE:
		name = "Cache Color Table"
	case ORDER_TYPE_CACHE_BITMAP_COMPRESSED:
		name = "Cache Bitmap (Compressed)"
	case ORDER_TYPE_CACHE_GLYPH:
		name = "Cache Glyph"
	case ORDER_TYPE_BITMAP_UNCOMPRESSED_V2:
		name = "Cache Bitmap V2"
	case ORDER_TYPE_BITMAP_COMPRESSED_V2:
		name = "Cache Bitmap V2 (Compressed)"
	case ORDER_TYPE_CACHE_BRUSH:
		name = "Cache Brush"
	case ORDER_TYPE_BITMAP_COMPRESSED_V3:
		name = "Cache Bitmap V3"
	}
	return fmt.Sprintf("[0x%02d] %s", s, name)
}

/* Alternate Secondary Drawing Orders */
const (
	ORDER_TYPE_SWITCH_SURFACE          = 0x00
	ORDER_TYPE_CREATE_OFFSCREEN_BITMAP = 0x01
	ORDER_TYPE_STREAM_BITMAP_FIRST     = 0x02
	ORDER_TYPE_STREAM_BITMAP_NEXT      = 0x03
	ORDER_TYPE_CREATE_NINE_GRID_BITMAP = 0x04
	ORDER_TYPE_GDIPLUS_FIRST           = 0x05
	ORDER_TYPE_GDIPLUS_NEXT            = 0x06
	ORDER_TYPE_GDIPLUS_END             = 0x07
	ORDER_TYPE_GDIPLUS_CACHE_FIRST     = 0x08
	ORDER_TYPE_GDIPLUS_CACHE_NEXT      = 0x09
	ORDER_TYPE_GDIPLUS_CACHE_END       = 0x0A
	ORDER_TYPE_WINDOW                  = 0x0B
	ORDER_TYPE_COMPDESK_FIRST          = 0x0C
	ORDER_TYPE_FRAME_MARKER            = 0x0D
)

const (
	GLYPH_FRAGMENT_NOP = 0x00
	GLYPH_FRAGMENT_USE = 0xFE
	GLYPH_FRAGMENT_ADD = 0xFF

	CBR2_HEIGHT_SAME_AS_WIDTH      = 0x01
	CBR2_PERSISTENT_KEY_PRESENT    = 0x02
	CBR2_NO_BITMAP_COMPRESSION_HDR = 0x08
	CBR2_DO_NOT_CACHE              = 0x10
)

const (
	ORDER_PRIMARY = iota
	ORDER_SECONDARY
	ORDER_ALTSEC
)

type OrderPdu struct {
	ControlFlags uint8
	Type         int
	Altsec       *Altsec
	Primary      *Primary
	Secondary    *Secondary
}

func (o *OrderPdu) HasBounds() bool {
	return o.ControlFlags&TS_BOUNDS != 0
}

type Altsec struct {
}

type Secondary struct {
}

type Primary struct {
	Bounds Bounds
	Data   PrimaryOrder
}

type FastPathOrdersPDU struct {
	NumberOrders uint16
	OrderPdus    []OrderPdu
}

func (*FastPathOrdersPDU) FastPathUpdateType() uint8 {
	return FASTPATH_UPDATETYPE_ORDERS
}

func (f *FastPathOrdersPDU) Unpack(r io.Reader) error {
	f.NumberOrders, _ = core.ReadUint16LE(r)
	//glog.Info("NumberOrders:", f.NumberOrders)
	for i := 0; i < int(f.NumberOrders); i++ {
		var o OrderPdu
		o.ControlFlags, _ = core.ReadUInt8(r)
		if o.ControlFlags&TS_STANDARD == 0 {
			//glog.Info("Altsec order")
			o.processAltsecOrder(r)
			o.Type = ORDER_ALTSEC
			//return errors.New("Not support")
		} else if o.ControlFlags&TS_SECONDARY != 0 {
			//glog.Info("Secondary order")
			o.processSecondaryOrder(r)
			o.Type = ORDER_SECONDARY
		} else {
			//glog.Info("Primary order")
			o.processPrimaryOrder(r)
			o.Type = ORDER_PRIMARY
		}

		if f.OrderPdus == nil {
			f.OrderPdus = make([]OrderPdu, 0, f.NumberOrders)
		}
		f.OrderPdus = append(f.OrderPdus, o)
	}
	return nil
}
func (o *OrderPdu) processAltsecOrder(r io.Reader) error {
	orderType := o.ControlFlags >> 2
	//glog.Info("Altsec:", orderType)
	switch orderType {
	case ORDER_TYPE_SWITCH_SURFACE:
	case ORDER_TYPE_CREATE_OFFSCREEN_BITMAP:
	case ORDER_TYPE_STREAM_BITMAP_FIRST:
	case ORDER_TYPE_STREAM_BITMAP_NEXT:
	case ORDER_TYPE_CREATE_NINE_GRID_BITMAP:
	case ORDER_TYPE_GDIPLUS_FIRST:
	case ORDER_TYPE_GDIPLUS_NEXT:
	case ORDER_TYPE_GDIPLUS_END:
	case ORDER_TYPE_GDIPLUS_CACHE_FIRST:
	case ORDER_TYPE_GDIPLUS_CACHE_NEXT:
	case ORDER_TYPE_GDIPLUS_CACHE_END:
	case ORDER_TYPE_WINDOW:
	case ORDER_TYPE_COMPDESK_FIRST:
	case ORDER_TYPE_FRAME_MARKER:
		core.ReadUInt32LE(r)
	}

	return nil
}
func (o *OrderPdu) processSecondaryOrder(r io.Reader) error {
	var sec Secondary
	length, _ := core.ReadUint16LE(r)
	flags, _ := core.ReadUint16LE(r)
	orderType, _ := core.ReadUInt8(r)

	glog.Info("Secondary:", SecondaryOrderType(orderType))

	b, _ := core.ReadBytes(int(length)+13-6, r)
	r0 := bytes.NewReader(b)

	switch orderType {
	case ORDER_TYPE_BITMAP_UNCOMPRESSED:
		fallthrough
	case ORDER_TYPE_CACHE_BITMAP_COMPRESSED:
		compressed := (orderType == ORDER_TYPE_CACHE_BITMAP_COMPRESSED)
		sec.updateCacheBitmapOrder(r0, compressed, flags)
	case ORDER_TYPE_BITMAP_UNCOMPRESSED_V2:
		fallthrough
	case ORDER_TYPE_BITMAP_COMPRESSED_V2:
		compressed := (orderType == ORDER_TYPE_BITMAP_COMPRESSED_V2)
		sec.updateCacheBitmapV2Order(r0, compressed, flags)
	case ORDER_TYPE_BITMAP_COMPRESSED_V3:
		sec.updateCacheBitmapV3Order(r0, flags)
	case ORDER_TYPE_CACHE_COLOR_TABLE:
		sec.updateCacheColorTableOrder(r0, flags)
	case ORDER_TYPE_CACHE_GLYPH:
		sec.updateCacheGlyphOrder(r0, flags)
	case ORDER_TYPE_CACHE_BRUSH:
		sec.updateCacheBrushOrder(r0, flags)
	default:
		glog.Debugf("Unsupport order type 0x%x", orderType)
	}

	return nil
}
func (b *Bounds) updateBounds(r io.Reader) {
	present, _ := core.ReadUInt8(r)

	if present&1 != 0 {
		readOrderCoord(r, &b.left, false)
	} else if present&16 != 0 {
		readOrderCoord(r, &b.left, true)
	}

	if present&2 != 0 {
		readOrderCoord(r, &b.top, false)
	} else if present&32 != 0 {
		readOrderCoord(r, &b.top, true)
	}

	if present&4 != 0 {
		readOrderCoord(r, &b.right, false)
	} else if present&64 != 0 {
		readOrderCoord(r, &b.right, true)
	}
	if present&8 != 0 {
		readOrderCoord(r, &b.bottom, false)
	} else if present&128 != 0 {
		readOrderCoord(r, &b.bottom, true)
	}
}

type PrimaryOrder interface {
	Type() int
	Unpack(io.Reader, uint32, bool) error
}

var (
	orderType uint8
	bounds    Bounds
)

func (o *OrderPdu) processPrimaryOrder(r io.Reader) error {
	o.Primary = &Primary{}
	if o.ControlFlags&TS_TYPE_CHANGE != 0 {
		orderType, _ = core.ReadUInt8(r)
	}
	size := 1
	switch orderType {
	case ORDER_TYPE_MEM3BLT, ORDER_TYPE_TEXT2:
		size = 3

	case ORDER_TYPE_PATBLT, ORDER_TYPE_MEMBLT, ORDER_TYPE_LINETO, ORDER_TYPE_POLYGON_CB, ORDER_TYPE_ELLIPSE_CB:
		size = 2
	}

	if o.ControlFlags&TS_ZERO_FIELD_BYTE_BIT0 != 0 {
		size--
	}
	if o.ControlFlags&TS_ZERO_FIELD_BYTE_BIT1 != 0 {
		if size < 2 {
			size = 0
		} else {
			size -= 2
		}
	}
	var present uint32
	for i := 0; i < size; i++ {
		bits, _ := core.ReadUInt8(r)
		present |= uint32(bits) << (i * 8)
	}

	if o.ControlFlags&TS_BOUNDS != 0 {
		if o.ControlFlags&TS_ZERO_BOUNDS_DELTAS == 0 {
			bounds.updateBounds(r)
		}
		//glog.Infof("updateBounds")
		o.Primary.Bounds = bounds
	}

	delta := o.ControlFlags&TS_DELTA_COORDINATES != 0

	//glog.Infof("present=%d,delta=%v", present, delta)

	var p PrimaryOrder
	switch orderType {
	case ORDER_TYPE_DSTBLT:
		p = &Dstblt{}

	case ORDER_TYPE_PATBLT:
		p = &Patblt{}

	case ORDER_TYPE_SCRBLT:
		p = &Scrblt{}

	//case ORDER_TYPE_DRAWNINEGRID:

	//case ORDER_TYPE_MULTI_DRAWNINEGRID:

	case ORDER_TYPE_LINETO:
		p = &LineTo{}

	case ORDER_TYPE_OPAQUERECT:
		p = &OpaqueRect{}

	case ORDER_TYPE_SAVEBITMAP:
		p = &SaveBitmap{}

	case ORDER_TYPE_MEMBLT:
		p = &Memblt{}

	case ORDER_TYPE_MEM3BLT:
		p = &Mem3blt{}

	//case ORDER_TYPE_MULTIDSTBLT:

	//case ORDER_TYPE_MULTIPATBLT:

	//case ORDER_TYPE_MULTISCRBLT:

	//case ORDER_TYPE_MULTIOPAQUERECT:

	//case ORDER_TYPE_FAST_INDEX:

	case ORDER_TYPE_POLYGON_SC:
		p = &PolygonSc{}

	case ORDER_TYPE_POLYGON_CB:
		p = &PolygonCb{}

	case ORDER_TYPE_POLYLINE:
		p = &Polyline{}

	//case ORDER_TYPE_FAST_GLYPH:

	case ORDER_TYPE_ELLIPSE_SC:
		p = &EllipeSc{}

	case ORDER_TYPE_ELLIPSE_CB:
		p = &EllipeCb{}

	case ORDER_TYPE_TEXT2:
		p = &GlayphIndex{}
	default:
		glog.Error("Not Support order type:", orderType)
		return errors.New("Not Support order type")
	}
	if p != nil {
		if err := p.Unpack(r, present, delta); err != nil {
			return err
		}
	}

	o.Primary.Data = p
	return nil
}
func readOrderCoord(r io.Reader, coord *int32, delta bool) {
	if delta {
		change, _ := core.ReadUInt8(r)
		*coord += int32(int8(change))
	} else {
		change, _ := core.ReadUint16LE(r)
		*coord = int32(int16(change))
	}
}

type Dstblt struct {
	x      int32
	y      int32
	cx     int32
	cy     int32
	opcode uint8
}

func (d *Dstblt) Type() int {
	return ORDER_TYPE_DSTBLT
}
func (d *Dstblt) Unpack(r io.Reader, present uint32, delta bool) error {
	glog.Infof("Dstblt Order")
	if present&0x01 != 0 {
		readOrderCoord(r, &d.x, delta)
	}
	if present&0x02 != 0 {
		readOrderCoord(r, &d.y, delta)
	}
	if present&0x04 != 0 {
		readOrderCoord(r, &d.cx, delta)
	}
	if present&0x08 != 0 {
		readOrderCoord(r, &d.cy, delta)
	}
	if present&0x10 != 0 {
		d.opcode, _ = core.ReadUInt8(r)
	}
	return nil
}

type Patblt struct {
	x        int32
	y        int32
	cx       int32
	cy       int32
	opcode   uint8
	bgcolour [4]uint8
	fgcolour [4]uint8
	brush    Brush
}

func (d *Patblt) Type() int {
	return ORDER_TYPE_PATBLT
}
func (d *Patblt) Unpack(r io.Reader, present uint32, delta bool) error {
	glog.Infof("Patblt Order")
	if present&0x01 != 0 {
		readOrderCoord(r, &d.x, delta)
	}
	if present&0x02 != 0 {
		readOrderCoord(r, &d.y, delta)
	}
	if present&0x04 != 0 {
		readOrderCoord(r, &d.cx, delta)
	}
	if present&0x08 != 0 {
		readOrderCoord(r, &d.cy, delta)
	}
	if present&0x10 != 0 {
		d.opcode, _ = core.ReadUInt8(r)
	}
	if present&0x0020 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.bgcolour[0], d.bgcolour[1], d.bgcolour[2], d.bgcolour[3] = b, g, r, a
	}
	if present&0x0040 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.fgcolour[0], d.fgcolour[1], d.fgcolour[2], d.fgcolour[3] = b, g, r, a
	}
	d.brush.updateBrush(r, present>>7)

	return nil
}

type Brush struct {
	X     uint8
	Y     uint8
	Style uint8
	Hatch uint8
	Data  []byte
}

func (b *Brush) updateBrush(r io.Reader, present uint32) {
	if present&1 != 0 {
		b.X, _ = core.ReadUInt8(r)
	}

	if present&2 != 0 {
		b.Y, _ = core.ReadUInt8(r)
	}

	if present&4 != 0 {
		b.Style, _ = core.ReadUInt8(r)
	}

	if present&8 != 0 {
		b.Hatch, _ = core.ReadUInt8(r)
	}

	if present&16 != 0 {
		data, _ := core.ReadBytes(7, r)
		b.Data = make([]byte, 0, 8)
		b.Data = append(b.Data, b.Hatch)
		b.Data = append(b.Data, data...)
	}
}

type Scrblt struct {
	X      int32
	Y      int32
	Cx     int32
	Cy     int32
	Opcode uint8
	Srcx   int32
	Srcy   int32
}

func (d *Scrblt) Type() int {
	return ORDER_TYPE_SCRBLT
}

var d Scrblt

func (d1 *Scrblt) Unpack(r io.Reader, present uint32, delta bool) error {
	glog.Infof("Scrblt Order")
	if present&0x0001 != 0 {
		readOrderCoord(r, &d.X, delta)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.Y, delta)
	}
	if present&0x0004 != 0 {
		readOrderCoord(r, &d.Cx, delta)
	}
	if present&0x0008 != 0 {
		readOrderCoord(r, &d.Cy, delta)
	}
	if present&0x0010 != 0 {
		d.Opcode, _ = core.ReadUInt8(r)
	}
	if present&0x0020 != 0 {
		readOrderCoord(r, &d.Srcx, delta)
	}
	if present&0x0040 != 0 {
		readOrderCoord(r, &d.Srcy, delta)
	}
	*d1 = d
	return nil
}

type LineTo struct {
	Mixmode  uint16
	Startx   int32
	Starty   int32
	Endx     int32
	Endy     int32
	Bgcolour [4]uint8
	Opcode   uint8
	Pen      Pen
}

func (d *LineTo) Type() int {
	return ORDER_TYPE_LINETO
}
func (d *LineTo) Unpack(r io.Reader, present uint32, delta bool) error {
	glog.Infof("LineTo Order")
	if present&0x0001 != 0 {
		d.Mixmode, _ = core.ReadUint16LE(r)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.Startx, delta)
	}
	if present&0x0004 != 0 {
		readOrderCoord(r, &d.Starty, delta)
	}
	if present&0x008 != 0 {
		readOrderCoord(r, &d.Endx, delta)
	}
	if present&0x0010 != 0 {
		readOrderCoord(r, &d.Endy, delta)
	}
	if present&0x0020 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.Bgcolour[0], d.Bgcolour[1], d.Bgcolour[2], d.Bgcolour[3] = b, g, r, a
	}
	if present&0x0040 != 0 {
		d.Opcode, _ = core.ReadUInt8(r)
	}

	d.Pen.updatePen(r, present>>7)

	return nil
}

type Pen struct {
	Style  uint8
	Width  uint8
	Colour [4]uint8
}

func (d *Pen) updatePen(r io.Reader, present uint32) {
	if present&1 != 0 {
		d.Style, _ = core.ReadUInt8(r)
	}

	if present&2 != 0 {
		d.Width, _ = core.ReadUInt8(r)
	}

	if present&4 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.Colour[0], d.Colour[1], d.Colour[2], d.Colour[3] = b, g, r, a
	}
}

type OpaqueRect struct {
	X      int32
	Y      int32
	Cx     int32
	Cy     int32
	Colour [4]uint8
}

func (d *OpaqueRect) Type() int {
	return ORDER_TYPE_OPAQUERECT
}
func (d *OpaqueRect) Unpack(r io.Reader, present uint32, delta bool) error {
	glog.Infof("OpaqueRect Order")
	if present&0x0001 != 0 {
		readOrderCoord(r, &d.X, delta)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.Y, delta)
	}
	if present&0x0004 != 0 {
		readOrderCoord(r, &d.Cx, delta)
	}
	if present&0x0008 != 0 {
		readOrderCoord(r, &d.Cy, delta)
	}
	if present&0x0010 != 0 {
		i, _ := core.ReadUInt8(r)
		d.Colour[0] = i
	}
	if present&0x0020 != 0 {
		i, _ := core.ReadUInt8(r)
		d.Colour[1] = i
	}
	if present&0x0040 != 0 {
		i, _ := core.ReadUInt8(r)
		d.Colour[2] = i
	}
	return nil
}

type SaveBitmap struct {
	Offset uint32
	Left   int32
	Top    int32
	Right  int32
	Bottom int32
	action uint8
}

func (d *SaveBitmap) Type() int {
	return ORDER_TYPE_SAVEBITMAP
}
func (d *SaveBitmap) Unpack(r io.Reader, present uint32, delta bool) error {
	if present&0x0001 != 0 {
		d.Offset, _ = core.ReadUInt32LE(r)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.Left, delta)
	}
	if present&0x0004 != 0 {
		readOrderCoord(r, &d.Top, delta)
	}
	if present&0x0008 != 0 {
		readOrderCoord(r, &d.Right, delta)
	}
	if present&0x0010 != 0 {
		readOrderCoord(r, &d.Bottom, delta)
	}
	if present&0x0020 != 0 {
		d.action, _ = core.ReadUInt8(r)
	}
	return nil
}

type Memblt struct {
	ColourTable uint8
	CacheId     uint8
	X           int32
	Y           int32
	Cx          int32
	Cy          int32
	Opcode      uint8
	Srcx        int32
	Srcy        int32
	CacheIdx    uint16
}

func (d *Memblt) Type() int {
	return ORDER_TYPE_MEMBLT
}
func (d *Memblt) Unpack(r io.Reader, present uint32, delta bool) error {
	if present&0x0001 != 0 {
		d.CacheId, _ = core.ReadUInt8(r)
		d.ColourTable, _ = core.ReadUInt8(r)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.X, delta)
	}
	if present&0x0004 != 0 {
		readOrderCoord(r, &d.Y, delta)
	}
	if present&0x0008 != 0 {
		readOrderCoord(r, &d.Cx, delta)
	}
	if present&0x0010 != 0 {
		readOrderCoord(r, &d.Cy, delta)
	}
	if present&0x0020 != 0 {
		d.Opcode, _ = core.ReadUInt8(r)
	}
	if present&0x0040 != 0 {
		readOrderCoord(r, &d.Srcx, delta)
	}
	if present&0x0080 != 0 {
		readOrderCoord(r, &d.Srcy, delta)
	}
	if present&0x0100 != 0 {
		d.CacheIdx, _ = core.ReadUint16LE(r)
	}
	return nil
}

type Mem3blt struct {
	ColourTable uint8
	CacheId     uint8
	X           int32
	Y           int32
	Cx          int32
	Cy          int32
	Opcode      uint8
	Srcx        int32
	Srcy        int32
	Bgcolour    [4]uint8
	Fgcolour    [4]uint8
	Brush       Brush
	CacheIdx    uint16
}

func (d *Mem3blt) Type() int {
	return ORDER_TYPE_MEM3BLT
}
func (d *Mem3blt) Unpack(r io.Reader, present uint32, delta bool) error {
	if present&0x000001 != 0 {
		d.CacheId, _ = core.ReadUInt8(r)
		d.ColourTable, _ = core.ReadUInt8(r)
	}
	if present&0x000002 != 0 {
		readOrderCoord(r, &d.X, delta)
	}
	if present&0x000004 != 0 {
		readOrderCoord(r, &d.Y, delta)
	}
	if present&0x000008 != 0 {
		readOrderCoord(r, &d.Cx, delta)
	}
	if present&0x000010 != 0 {
		readOrderCoord(r, &d.Cy, delta)
	}
	if present&0x000020 != 0 {
		d.Opcode, _ = core.ReadUInt8(r)
	}
	if present&0x000040 != 0 {
		readOrderCoord(r, &d.Srcx, delta)
	}
	if present&0x000080 != 0 {
		readOrderCoord(r, &d.Srcy, delta)
	}
	if present&0x000100 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.Bgcolour[0], d.Bgcolour[1], d.Bgcolour[2], d.Bgcolour[3] = b, g, r, a
	}
	if present&0x000200 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.Fgcolour[0], d.Fgcolour[1], d.Fgcolour[2], d.Fgcolour[3] = b, g, r, a
	}
	d.Brush.updateBrush(r, present>>10)
	if present&0x008000 != 0 {
		d.CacheIdx, _ = core.ReadUint16LE(r)
	}
	if present&0x010000 != 0 {
		core.ReadUint16LE(r)
	}

	return nil
}

type PolygonSc struct {
	X        int32
	Y        int32
	Opcode   uint8
	Fillmode uint8
	Fgcolour [4]uint8
	Npoints  uint8
	Points   []Point
}

type Point struct {
	X int32
	Y int32
}

func (d *PolygonSc) Type() int {
	return ORDER_TYPE_POLYGON_SC
}
func (d *PolygonSc) Unpack(r io.Reader, present uint32, delta bool) error {
	if present&0x0001 != 0 {
		readOrderCoord(r, &d.X, delta)
	}
	if present&0x0002 != 0 {
		readOrderCoord(r, &d.Y, delta)
	}
	if present&0x0004 != 0 {
		d.Opcode, _ = core.ReadUInt8(r)
	}
	if present&0x0008 != 0 {
		d.Fillmode, _ = core.ReadUInt8(r)
	}
	if present&0x0010 != 0 {
		b, g, r, a := updateReadColorRef(r)
		d.Fgcolour[0], d.Fgcolour[1], d.Fgcolour[2], d.Fgcolour[3] = b, g, r, a
	}
	if present&0x0020 != 0 {
		d.Npoints, _ = core.ReadUInt8(r)
		d.Points = make([]Point, 0, d.Npoints+1)
	}
	if present&0x0040 != 0 {
		size, _ := core.ReadUInt8(r)
		data, _ := core.ReadBytes(int(size), r)
		d.Points = append(d.Points, Point{d.X, d.Y})
		var flags uint8
		r = bytes.NewReader(data)
		for i := 1; i <= int(d.Npoints); i++ {
			var p Point
			if (i-1)%4 == 0 {
				flags, _ = core.ReadUInt8(r)
			}
			if (^flags)&0x80 != 0 {
				p.X = parseDelta(r)
			}
			if (^flags)&0x40 != 0 {
				p.Y = parseDelta(r)
			}
			flags <<= 2
		}
	}

	return nil
}

func parseDelta(r io.Reader) (v int32) {
	b, _ := core.ReadUInt8(r)
	if b&0x40 != 0 {
		v = int32(b) | (^0x3F)
	} else {
		v = int32(b & 0x3F)
	}
	if b&0x80 != 0 {
		b, _ := core.ReadUInt8(r)
		v = (v << 8) | int32(b)
	}
	return
}

type PolygonCb struct {
}

func (d *PolygonCb) Type() int {
	return ORDER_TYPE_POLYGON_CB
}
func (d *PolygonCb) Unpack(r io.Reader, present uint32, delta bool) error {
	return nil
}

type Polyline struct {
}

func (d *Polyline) Type() int {
	return ORDER_TYPE_POLYLINE
}
func (d *Polyline) Unpack(r io.Reader, present uint32, delta bool) error {
	return nil
}

type EllipeSc struct {
}

func (d *EllipeSc) Type() int {
	return ORDER_TYPE_ELLIPSE_SC
}
func (d *EllipeSc) Unpack(r io.Reader, present uint32, delta bool) error {
	return nil
}

type EllipeCb struct {
}

func (d *EllipeCb) Type() int {
	return ORDER_TYPE_ELLIPSE_CB
}
func (d *EllipeCb) Unpack(r io.Reader, present uint32, delta bool) error {
	return nil
}

type GlayphIndex struct {
}

func (d *GlayphIndex) Type() int {
	return ORDER_TYPE_TEXT2
}
func (d *GlayphIndex) Unpack(r io.Reader, present uint32, delta bool) error {
	return nil
}

/*Secondary*/
func (s *Secondary) updateCacheBitmapOrder(r io.Reader, compressed bool, flags uint16) {
	var cb CacheBitmapOrder
	cb.cacheId, _ = core.ReadUInt8(r)
	core.ReadUInt8(r)
	cb.bitmapWidth, _ = core.ReadUInt8(r)
	cb.bitmapHeight, _ = core.ReadUInt8(r)
	cb.bitmapBpp, _ = core.ReadUInt8(r)
	bitmapLength, _ := core.ReadUint16LE(r)
	cb.cacheIndex, _ = core.ReadUint16LE(r)
	var bitmapComprHdr []byte
	if compressed {
		if (flags & NO_BITMAP_COMPRESSION_HDR) == 0 {
			bitmapComprHdr, _ = core.ReadBytes(8, r)
			bitmapLength -= 8
		}
	}
	cb.bitmapComprHdr = bitmapComprHdr
	cb.bitmapDataStream, _ = core.ReadBytes(int(bitmapLength), r)
	cb.bitmapLength = bitmapLength

}

type CacheBitmapOrder struct {
	cacheId          uint8
	bitmapBpp        uint8
	bitmapWidth      uint8
	bitmapHeight     uint8
	bitmapLength     uint16
	cacheIndex       uint16
	bitmapComprHdr   []byte
	bitmapDataStream []byte
}

func getCbV2Bpp(bpp uint32) (b uint32) {
	switch bpp {
	case 3:
		b = 8
	case 4:
		b = 16
	case 5:
		b = 24
	case 6:
		b = 32
	default:
		b = 0
	}
	return
}

type CacheBitmapV2Order struct {
	cacheId            uint32
	flags              uint32
	key1               uint32
	key2               uint32
	bitmapBpp          uint32
	bitmapWidth        uint8
	bitmapHeight       uint8
	bitmapLength       uint16
	cacheIndex         uint32
	compressed         bool
	cbCompFirstRowSize uint16
	cbCompMainBodySize uint16
	cbScanWidth        uint16
	cbUncompressedSize uint16
	bitmapDataStream   []byte
}

func (s *Secondary) updateCacheBitmapV2Order(r io.Reader, compressed bool, flags uint16) {
	var cb CacheBitmapV2Order
	cb.cacheId = uint32(flags) & 0x0003
	cb.flags = (uint32(flags) & 0xFF80) >> 7
	bitsPerPixelId := (uint32(flags) & 0x0078) >> 3
	cb.bitmapBpp = getCbV2Bpp(bitsPerPixelId)

	if cb.flags&CBR2_PERSISTENT_KEY_PRESENT != 0 {
		cb.key1, _ = core.ReadUInt32LE(r)
		cb.key2, _ = core.ReadUInt32LE(r)
	}

	if cb.flags&CBR2_HEIGHT_SAME_AS_WIDTH != 0 {
		cb.bitmapWidth, _ = core.ReadUInt8(r)
		cb.bitmapHeight = cb.bitmapWidth
	} else {
		cb.bitmapWidth, _ = core.ReadUInt8(r)
		cb.bitmapHeight, _ = core.ReadUInt8(r)
	}

	bitmapLength, _ := core.ReadUint16LE(r)
	cacheIndex, _ := core.ReadUInt8(r)

	if cb.flags&CBR2_DO_NOT_CACHE != 0 {
		cb.cacheIndex = 0x7FFF
	} else {
		cb.cacheIndex = uint32(cacheIndex)
	}

	if compressed {
		if cb.flags&CBR2_NO_BITMAP_COMPRESSION_HDR == 0 {
			cb.cbCompFirstRowSize, _ = core.ReadUint16LE(r)
			cb.cbCompMainBodySize, _ = core.ReadUint16LE(r)
			cb.cbScanWidth, _ = core.ReadUint16LE(r)
			cb.cbUncompressedSize, _ = core.ReadUint16LE(r)
			bitmapLength = cb.cbCompMainBodySize
		}
	}

	cb.bitmapDataStream, _ = core.ReadBytes(int(bitmapLength), r)
	cb.bitmapLength = bitmapLength
	cb.compressed = compressed

}

type CacheBitmapV3Order struct {
	cacheId    uint32
	bpp        uint32
	flags      uint32
	cacheIndex uint16
	key1       uint32
	key2       uint32
	bitmapData BitmapDataEx
}
type BitmapDataEx struct {
	bpp     uint8
	codecID uint8
	width   uint16
	height  uint16
	length  uint32
	data    []byte
}

func (s *Secondary) updateCacheBitmapV3Order(r io.Reader, flags uint16) {
	var cb CacheBitmapV3Order

	cb.cacheId = uint32(flags) & 0x00000003
	cb.flags = (uint32(flags) & 0x0000FF80) >> 7
	bitsPerPixelId := (uint32(flags) & 0x00000078) >> 3
	cb.bpp = getCbV2Bpp(bitsPerPixelId)

	cacheIndex, _ := core.ReadUint16LE(r)
	cb.cacheIndex = cacheIndex
	cb.key1, _ = core.ReadUInt32LE(r)
	cb.key2, _ = core.ReadUInt32LE(r)

	bitmapData := &cb.bitmapData
	bitmapData.bpp, _ = core.ReadUInt8(r)
	core.ReadUInt8(r)
	core.ReadUInt8(r)
	bitmapData.codecID, _ = core.ReadUInt8(r)
	bitmapData.width, _ = core.ReadUint16LE(r)
	bitmapData.height, _ = core.ReadUint16LE(r)
	new_len, _ := core.ReadUInt32LE(r)

	bitmapData.data, _ = core.ReadBytes(int(new_len), r)
	bitmapData.length = new_len

}

type CacheColorTableOrder struct {
	cacheIndex   uint8
	numberColors uint16
	colorTable   [256 * 4]uint8
}

func (s *Secondary) updateCacheColorTableOrder(r io.Reader, flags uint16) {
	var cb CacheColorTableOrder
	cb.cacheIndex, _ = core.ReadUInt8(r)
	cb.numberColors, _ = core.ReadUint16LE(r)

	if cb.numberColors != 256 {
		/* This field MUST be set to 256 */
		return
	}

	for i := 0; i < int(cb.numberColors)*4; i++ {
		cb.colorTable[i], cb.colorTable[i+1], cb.colorTable[i+2], cb.colorTable[i+3] = updateReadColorRef(r)
	}
}
func updateReadColorRef(r io.Reader) (uint8, uint8, uint8, uint8) {
	blue, _ := core.ReadUInt8(r)
	green, _ := core.ReadUInt8(r)
	red, _ := core.ReadUInt8(r)
	core.ReadUInt8(r)

	return blue, green, red, 255
}

type CacheGlyphOrder struct {
	cacheId uint8
	nglyphs uint8
	glyphs  []CacheGlyph
}
type CacheGlyph struct {
	character uint16
	offset    uint16
	baseline  uint16
	width     uint16
	height    uint16
	datasize  int
	data      []uint8
}

func (s *Secondary) updateCacheGlyphOrder(r io.Reader, flags uint16) {
	var cb CacheGlyphOrder

	cb.cacheId, _ = core.ReadUInt8(r)
	cb.nglyphs, _ = core.ReadUInt8(r)
	cb.glyphs = make([]CacheGlyph, 0, cb.nglyphs)

	for i := 0; i < int(cb.nglyphs); i++ {
		var c CacheGlyph
		c.character, _ = core.ReadUint16LE(r)
		c.offset, _ = core.ReadUint16LE(r)
		c.baseline, _ = core.ReadUint16LE(r)
		c.width, _ = core.ReadUint16LE(r)
		c.height, _ = core.ReadUint16LE(r)

		c.datasize = int(c.height*((c.width+7)/8)+3) & ^3
		c.data, _ = core.ReadBytes(c.datasize, r)

		cb.glyphs = append(cb.glyphs, c)
	}
}

type CacheBrushOrder struct {
	index  uint8
	bpp    uint8
	cx     uint8
	cy     uint8
	style  uint8
	length uint8
	data   []uint8
}

func (s *Secondary) updateCacheBrushOrder(r io.Reader, flags uint16) {
	var cb CacheBrushOrder
	cb.index, _ = core.ReadUInt8(r)
	cb.bpp, _ = core.ReadUInt8(r)
	cb.cx, _ = core.ReadUInt8(r)
	cb.cy, _ = core.ReadUInt8(r)
	cb.style, _ = core.ReadUInt8(r)
	cb.length, _ = core.ReadUInt8(r)
	if cb.cx == 8 && cb.cy == 8 {
		if cb.bpp == 1 {
			for i := 7; i >= 0; i-- {
				cb.data[i], _ = core.ReadUInt8(r)
			}
		} else {
			bpp := int(cb.bpp) - 2
			if int(cb.length) == 16+4*bpp {
				/* compressed brush */
				data, _ := core.ReadBytes(int(cb.length), r)
				cb.data = update_decompress_brush(data, bpp)
			} else {
				/* uncompressed brush */
				scanline := 8 * 8 * bpp
				cb.data, _ = core.ReadBytes(scanline, r)
			}
		}
	}
}
func update_decompress_brush(in []uint8, bpp int) []uint8 {
	var pal_index, in_index, shift int

	pal := in[16:]
	out := make([]uint8, 8*8*bpp)
	/* read it bottom up */
	for y := 7; y >= 0; y-- {
		/* 2 bytes per row */
		x := 0
		for do2 := 0; do2 < 2; do2++ {
			/* 4 pixels per byte */
			shift = 6
			for shift >= 0 {
				pal_index = int((in[in_index] >> shift) & 3)
				/* size of palette entries depends on bpp */
				for i := 0; i < bpp; i++ {
					out[(y*8+x)*bpp+i] = pal[pal_index*bpp+i]
				}
				x++
				shift -= 2
			}
			in_index++
		}
	}

	return out
}

/*Primary*/
type Bounds struct {
	left   int32
	top    int32
	right  int32
	bottom int32
}
type OrderInfo struct {
	controlFlags     uint32
	orderType        uint32
	fieldFlags       uint32
	boundsFlags      uint32
	bounds           Bounds
	deltaCoordinates bool
}
