// // Package epd provides driver for Waveshare's E-paper e-ink display
package display // import "go.riyazali.net/epd"

// import (
// 	"errors"
// 	"image/color"
// 	"math"
// 	"time"

// 	"github.com/fogleman/gg"
// )

// // Display resolution
// const EPD_WIDTH = 122
// const EPD_HEIGHT = 250
// const (
// 	DRIVER_OUTPUT_CONTROL                = 0x01
// 	BOOSTER_SOFT_START_CONTROL           = 0x0C
// 	GATE_SCAN_START_POSITION             = 0x0F
// 	DEEP_SLEEP_MODE                      = 0x10
// 	DATA_ENTRY_MODE_SETTING              = 0x11
// 	SW_RESET                             = 0x12
// 	TEMPERATURE_SENSOR_CONTROL           = 0x1A
// 	MASTER_ACTIVATION                    = 0x20
// 	DISPLAY_UPDATE_CONTROL_1             = 0x21
// 	DISPLAY_UPDATE_CONTROL_2             = 0x22
// 	WRITE_RAM                            = 0x24
// 	WRITE_VCOM_REGISTER                  = 0x2C
// 	WRITE_LUT_REGISTER                   = 0x32
// 	SET_DUMMY_LINE_PERIOD                = 0x3A
// 	SET_GATE_TIME                        = 0x3B
// 	BORDER_WAVEFORM_CONTROL              = 0x3C
// 	SET_RAM_X_ADDRESS_START_END_POSITION = 0x44
// 	SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45
// 	SET_RAM_X_ADDRESS_COUNTER            = 0x4E
// 	SET_RAM_Y_ADDRESS_COUNTER            = 0x4F
// 	TERMINATE_FRAME_READ_WRITE           = 0xFF
// 	SET_ANALOG_BLOCK_CONTROL             = 0x74
// 	SET_DIGITAL_BLOCK_CONTROL            = 0x7E

// 	ROTATION_0   Rotation = 0
// 	ROTATION_90  Rotation = 1 // 90 degrees clock-wise rotation
// 	ROTATION_180 Rotation = 2
// 	ROTATION_270 Rotation = 3
// )

// // ErrInvalidImageSize is returned if the given image bounds doesn't fit into display bounds
// var ErrInvalidImageSize = errors.New("invalid image size")

// type Rotation uint8

// type Config struct {
// 	Width        int16 // Width is the display resolution
// 	Height       int16
// 	LogicalWidth int16    // LogicalWidth must be a multiple of 8 and same size or bigger than Width
// 	Rotation     Rotation // Rotation is clock-wise
// }

// // WriteablePin is a GPIO pin through which the driver can write digital data
// type WriteablePin interface {
// 	// High sets the pins output to digital high
// 	High()

// 	// Low sets the pins output to digital low
// 	Low()
// }

// // ReadablePin is a GPIO pin through which the driver can read digital data
// type ReadablePin interface {
// 	// Read reads from the pin and return the data as a byte
// 	Read() uint8
// }

// // Transmit is a function that sends the data payload across to the device via the SPI line
// type Transmit func(data ...byte)

// // fullUpdate is a lookup table used whilst in full update mode
// var fullUpdateLut = [286]uint8{
// 	0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT0: BB:     VS 0 ~7
// 	0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT1: BW:     VS 0 ~7
// 	0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT2: WB:     VS 0 ~7
// 	0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT3: WW:     VS 0 ~7
// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM:   VS 0 ~7

// 	0x03, 0x03, 0x00, 0x00, 0x02, // TP0 A~D RP0
// 	0x09, 0x09, 0x00, 0x00, 0x02, // TP1 A~D RP1
// 	0x03, 0x03, 0x00, 0x00, 0x02, // TP2 A~D RP2
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6

// 	0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A,
// }

// // partialUpdate is a lookup table used whilst in partial update mode

// var partialUpdateLut = [286]uint8{
// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT0: BB:     VS 0 ~7
// 	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT1: BW:     VS 0 ~7
// 	0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT2: WB:     VS 0 ~7
// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT3: WW:     VS 0 ~7
// 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM:   VS 0 ~7

// 	0x0A, 0x00, 0x00, 0x00, 0x00, // TP0 A~D RP0
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP1 A~D RP1
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP2 A~D RP2
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5
// 	0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6

// 	0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A,
// }

// // EPD defines the base type for the e-paper display driver
// type EPD struct {
// 	// pins used by this driver
// 	rst  WriteablePin // for reset signal
// 	dc   WriteablePin // for data/command select signal; D=HIGH C=LOW
// 	cs   WriteablePin // for chip select signal; this pin is active low
// 	busy ReadablePin  // for reading in busy signal

// 	// SPI transmitter
// 	transmit Transmit

// 	logicalWidth int16
// 	width        int16
// 	height       int16
// 	buffer       []uint8
// 	bufferLength uint32
// 	rotation     Rotation

// 	Update Update
// }

// // New creates a new EPD device driver
// func New(rst, dc, cs WriteablePin, busy ReadablePin, transmit Transmit) *EPD {
// 	return &EPD{
// 		rst:      rst,
// 		dc:       dc,
// 		cs:       cs,
// 		busy:     busy,
// 		Update:   FullUpdate,
// 		transmit: transmit,
// 	}
// }

// // reset resets the display back to defaults
// func (epd *EPD) Reset() {
// 	epd.rst.High()
// 	time.Sleep(200 * time.Millisecond)
// 	epd.rst.Low()
// 	time.Sleep(5 * time.Millisecond)
// 	epd.rst.High()
// 	time.Sleep(200 * time.Millisecond)
// }

// // command transmits single byte of command instruction over the SPI line
// func (epd *EPD) sendCommand(c byte) {
// 	epd.dc.Low()
// 	epd.cs.Low()
// 	epd.transmit(c)
// 	epd.cs.High()
// }

// // data transmits single byte of data payload over SPI line
// func (epd *EPD) sendData(d byte) {
// 	epd.dc.High()
// 	epd.cs.Low()
// 	epd.transmit(d)
// 	epd.cs.High()
// }

// // idle reads from busy line and waits for the device to get into idle state
// func (epd *EPD) ReadBusy() {
// 	for epd.busy.Read() == 0x01 {
// 		time.Sleep(100 * time.Millisecond)
// 	}
// }

// // turnOnDisplay activates the display and renders the image that's there in the device's RAM
// func (epd *EPD) turnOnDisplay() {
// 	epd.sendCommand(DISPLAY_UPDATE_CONTROL_2)
// 	epd.sendData(0xC7)
// 	epd.sendCommand(MASTER_ACTIVATION)
// 	epd.ReadBusy()
// }

// // turnOnDisplay activates the display and renders the image that's there in the device's RAM
// func (epd *EPD) turnOnDisplayPartial() {
// 	epd.sendCommand(DISPLAY_UPDATE_CONTROL_2)
// 	epd.sendData(0x0C)
// 	epd.sendCommand(MASTER_ACTIVATION)
// 	epd.ReadBusy()
// }

// type Update int64

// const (
// 	FullUpdate    Update = 0
// 	PartialUpdate Update = 1
// )

// // mode sets the device's mode (based on the LookupTable)
// // The device can either be in FullUpdate mode where the whole display is updated each time an image is rendered
// // or in PartialUpdate mode where only the changed section is updated (and it doesn't cause any flicker)
// //
// // Waveshare recommends doing full update of the display at least once per-day to prevent ghost image problems
// func (epd *EPD) Configure(cfg Config) {

// 	if cfg.LogicalWidth != 0 {
// 		epd.logicalWidth = cfg.LogicalWidth
// 	} else {
// 		epd.logicalWidth = EPD_WIDTH
// 	}
// 	if cfg.Width != 0 {
// 		epd.width = cfg.Width
// 	} else {
// 		epd.width = EPD_WIDTH
// 	}
// 	if cfg.Height != 0 {
// 		epd.height = cfg.Height
// 	} else {
// 		epd.height = EPD_HEIGHT
// 	}
// 	epd.rotation = cfg.Rotation
// 	epd.bufferLength = (uint32(epd.logicalWidth) * uint32(epd.height)) / 8
// 	epd.buffer = make([]uint8, epd.bufferLength)
// 	for i := uint32(0); i < epd.bufferLength; i++ {
// 		epd.buffer[i] = 0xFF
// 	}

// 	epd.Reset()

// 	if epd.Update == FullUpdate {

// 		epd.ReadBusy()
// 		epd.sendCommand(SW_RESET)
// 		epd.ReadBusy()

// 		epd.sendCommand(SET_ANALOG_BLOCK_CONTROL) //set analog block control
// 		epd.sendData(0x54)
// 		epd.sendCommand(SET_DIGITAL_BLOCK_CONTROL) //set digital block control
// 		epd.sendData(SET_GATE_TIME)

// 		epd.sendCommand(DRIVER_OUTPUT_CONTROL) //Driver output control
// 		epd.sendData(0xF9)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)

// 		epd.sendCommand(DATA_ENTRY_MODE_SETTING) //data entry mode
// 		epd.sendData(SET_GATE_TIME)

// 		epd.sendCommand(SET_RAM_X_ADDRESS_START_END_POSITION) //set Ram-X address start/end position
// 		epd.sendData(0x00)
// 		epd.sendData(GATE_SCAN_START_POSITION) //0x0C-->(15+1)*8=128

// 		epd.sendCommand(SET_RAM_Y_ADDRESS_START_END_POSITION) //set Ram-Y address start/end position
// 		epd.sendData(0xF9)                                    //0xF9-->(249+1)=250
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)

// 		epd.sendCommand(BORDER_WAVEFORM_CONTROL) //BorderWavefrom
// 		epd.sendData(0x03)

// 		epd.sendCommand(WRITE_VCOM_REGISTER) //VCOM Voltage
// 		epd.sendData(0x55)                   //

// 		epd.sendCommand(0x03)
// 		epd.sendData(fullUpdateLut[70])

// 		epd.sendCommand(0x04) //
// 		epd.sendData(fullUpdateLut[71])
// 		epd.sendData(fullUpdateLut[72])
// 		epd.sendData(fullUpdateLut[73])

// 		epd.sendCommand(SET_DUMMY_LINE_PERIOD) //Dummy Line
// 		epd.sendData(fullUpdateLut[74])
// 		epd.sendCommand(SET_GATE_TIME) //Gate time
// 		epd.sendData(fullUpdateLut[75])

// 		epd.sendCommand(WRITE_LUT_REGISTER)
// 		for count := 0; count < 70; count++ {
// 			epd.sendData(fullUpdateLut[count])
// 		}

// 		epd.sendCommand(SET_RAM_X_ADDRESS_COUNTER) // set RAM x address count to 0
// 		epd.sendData(0x00)
// 		epd.sendCommand(SET_RAM_Y_ADDRESS_COUNTER) // set RAM y address count to 0X127
// 		epd.sendData(0xF9)
// 		epd.sendData(0x00)
// 		epd.ReadBusy()

// 	} else {

// 		epd.sendCommand(WRITE_VCOM_REGISTER) //VCOM Voltage
// 		epd.sendData(0x26)

// 		epd.ReadBusy()

// 		epd.sendCommand(WRITE_LUT_REGISTER)
// 		for count := 0; count < 70; count++ {
// 			epd.sendData(partialUpdateLut[count])
// 		}

// 		epd.sendCommand(0x37)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)
// 		epd.sendData(0x40)
// 		epd.sendData(0x00)
// 		epd.sendData(0x00)

// 		epd.sendCommand(DISPLAY_UPDATE_CONTROL_2)
// 		epd.sendData(0xC0)
// 		epd.sendCommand(MASTER_ACTIVATION)
// 		epd.ReadBusy()

// 		epd.sendCommand(BORDER_WAVEFORM_CONTROL) //BorderWavefrom
// 		epd.sendData(0x01)
// 	}

// }

// // setMemoryArea sets the area of the display that will be updated
// func (epd *EPD) setMemoryArea(x0 int16, y0 int16, x1 int16, y1 int16) {
// 	epd.sendCommand(SET_RAM_X_ADDRESS_START_END_POSITION)
// 	epd.sendData(uint8((x0 >> 3) & 0xFF))
// 	epd.sendData(uint8((x1 >> 3) & 0xFF))
// 	epd.sendCommand(SET_RAM_Y_ADDRESS_START_END_POSITION)
// 	epd.sendData(uint8(y0 & 0xFF))
// 	epd.sendData(uint8((y0 >> 8) & 0xFF))
// 	epd.sendData(uint8(y1 & 0xFF))
// 	epd.sendData(uint8((y1 >> 8) & 0xFF))
// }

// // setMemoryPointer moves the internal pointer to the speficied coordinates
// func (epd *EPD) setMemoryPointer(x int16, y int16) {
// 	epd.sendCommand(SET_RAM_X_ADDRESS_COUNTER)
// 	epd.sendData(uint8((x >> 3) & 0xFF))
// 	epd.sendCommand(SET_RAM_Y_ADDRESS_COUNTER)
// 	epd.sendData(uint8(y & 0xFF))
// 	epd.sendData(uint8((y >> 8) & 0xFF))
// 	epd.ReadBusy()
// }
// func (epd *EPD) Draw(img *gg.Context) error {
// 	// fmt.Println(img.Bounds())
// 	// for i := 0; i < int(img.Bounds().Max.Y); i++ {
// 	// 	for j := 0; j < int(img.Bounds().Max.X); j++ {

// 	// 		var pixel = img.At(i, j)
// 	// 		r, g, b, a := pixel.RGBA()
// 	// 		var c color.RGBA = color.RGBA{
// 	// 			R: uint8(r),
// 	// 			G: uint8(g),
// 	// 			B: uint8(b),
// 	// 			A: uint8(a),
// 	// 		}
// 	// 		epd.SetPixel(int16(i), int16(j), c)
// 	// 		// }
// 	// 	}
// 	// }

// 	epd.Display(img)
// 	return nil
// }

// // SetPixel modifies the internal buffer in a single pixel.
// // The display have 2 colors: black and white
// // We use RGBA(0,0,0, 255) as white (transparent)
// // Anything else as black
// func (epd *EPD) SetPixel(x int16, y int16, c color.RGBA) {
// 	x, y = epd.xy(x, y)
// 	if x < 0 || x >= epd.logicalWidth || y < 0 || y >= epd.height {
// 		return
// 	}
// 	byteIndex := (x + y*epd.logicalWidth) / 8

// 	for px := 0; px < 8; px++ {
// 		if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
// 			epd.buffer[byteIndex] |= 0x80 >> uint8(x%8)
// 		} else { // WHITE / EMPTY
// 			epd.buffer[byteIndex] &= ^(0x80 >> (x % 8))
// 		}
// 	}
// }

// // Display sends the buffer to the screen.
// func (epd *EPD) Display(img *gg.Context) error {
// 	// img.RotateAbout(PI/2, float64(epd.height/2), float64(epd.width)/2)
// 	// img.Translate(float64(epd.height/2), 0)
// 	// epd.setMemoryArea(0, 0, epd.logicalWidth-1, epd.height-1)
// 	// for j := int16(0); j < epd.height; j++ {
// 	// 	epd.setMemoryPointer(0, j)
// 	// 	epd.sendCommand(WRITE_RAM)
// 	// 	for i := 0; i < int(epd.logicalWidth); i++ {
// 	// 		// this loop converts individual pixels into a single byte
// 	// 		// 8-pixels at a time and then sends that byte to render
// 	// 		var b = 0xFF
// 	// 		it, jt := epd.xy(int16(i), int16(j))
// 	// 		_ = it
// 	// 		_ = jt
// 	// 		var pixel = img.At(int(it), int(jt))
// 	// 		for px := int16(0); px < 8; px++ {
// 	// 			if isdark(pixel.RGBA()) {
// 	// 				b &= ^(0x80 >> (px % 8))
// 	// 			}
// 	// 		}
// 	// 		epd.sendData(byte(b))
// 	// 	}
// 	// }

// 	epd.setMemoryArea(0, 0, epd.logicalWidth-1, epd.height-1)
// 	for j := int16(0); j < epd.height; j++ {
// 		epd.setMemoryPointer(0, j)
// 		epd.sendCommand(WRITE_RAM)
// 		for i := int16(0); i < epd.logicalWidth; i += 8 {
// 			var b = 0xFF
// 			for px := 0; px < 8; px++ {
// 				i2, j2 := epd.xy(i, j)
// 				var pixel = img.Image().At(int(i2)+px, int(j2))
// 				if isdark(pixel.RGBA()) {
// 					b &= ^(0x80 >> (px % 8))
// 				}
// 			}
// 			epd.sendData(byte(b))
// 		}
// 	}

// 	epd.turnOnDisplay()
// 	return nil
// }

// // Sleep puts the device into "deep sleep" mode where it draws zero (0) current
// //
// // Waveshare recommends putting the device in "deep sleep" mode (or disconnect from power)
// // if doesn't need updating/refreshing.
// func (epd *EPD) Sleep() {
// 	epd.sendCommand(DEEP_SLEEP_MODE)
// 	epd.sendData(0x03)
// }

// // xy chages the coordinates according to the rotation
// func (d *EPD) xy(x, y int16) (int16, int16) {
// 	switch d.rotation {
// 	case ROTATION_0:
// 		return x, y
// 	case ROTATION_90:
// 		return d.width - y - 1, x
// 	case ROTATION_180:
// 		return d.width - x - 1, d.height - y - 1
// 	case ROTATION_270:
// 		return y, d.height - x - 1
// 	}
// 	return x, y
// }

// // ClearBuffer sets the buffer to 0xFF (white)
// func (epd *EPD) ClearBuffer() {
// 	for i := uint32(0); i < epd.bufferLength; i++ {
// 		epd.buffer[i] = 0xFF
// 	}
// }

// // ClearDisplay erases the device SRAM
// func (epd *EPD) ClearDisplay() {
// 	epd.setMemoryArea(0, 0, epd.logicalWidth-1, epd.height-1)
// 	epd.setMemoryPointer(0, 0)
// 	epd.sendCommand(WRITE_RAM)
// 	for i := uint32(0); i < epd.bufferLength; i++ {
// 		epd.sendData(0xFF)
// 	}
// 	// epd.Display()
// }

// // Size returns the current size of the display.
// func (d *EPD) Size() (w, h int16) {
// 	if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
// 		return d.height, d.logicalWidth
// 	}
// 	return d.logicalWidth, d.height
// }

// // // ClearDisplay erases the device SRAM
// // func (d *EPD) ClearDisplay() {
// // 	d.setMemoryArea(0, 0, d.logicalWidth-1, d.height-1)
// // 	d.setMemoryPointer(0, 0)
// // 	d.sendCommand(WRITE_RAM)
// // 	for i := uint32(0); i < d.bufferLength; i++ {
// // 		d.sendData(0xFF)
// // 	}
// // 	d.draw()
// // }

// // SetRotation changes the rotation (clock-wise) of the device
// func (d *EPD) SetRotation(rotation Rotation) {
// 	d.rotation = rotation
// }

// // isdark is a utility method which returns true if the pixel color is considered dark else false
// // this function is taken from https://git.io/JviWg
// func isdark(r, g, b, _ uint32) bool {
// 	return math.Sqrt(
// 		0.299*math.Pow(float64(r), 2)+
// 			0.587*math.Pow(float64(g), 2)+
// 			0.114*math.Pow(float64(b), 2)) <= 130
// }

// // // Package epd provides driver for Waveshare's E-paper e-ink display
// // package epd  // import "go.riyazali.net/epd"

// // import (
// // 	"errors"
// // 	"image"
// // 	"image/color"
// // 	"math"
// // 	"time"
// // )

// // // ErrInvalidImageSize is returned if the given image bounds doesn't fit into display bounds
// // var ErrInvalidImageSize = errors.New("invalid image size")

// // // LookupTable defines a type holding the instruction lookup table
// // // This lookup table is used by the device when performing refreshes
// // type Mode uint8

// // // WriteablePin is a GPIO pin through which the driver can write digital data
// // type WriteablePin interface {
// // 	// High sets the pins output to digital high
// // 	High()

// // 	// Low sets the pins output to digital low
// // 	Low()
// // }

// // // ReadablePin is a GPIO pin through which the driver can read digital data
// // type ReadablePin interface {
// // 	// Read reads from the pin and return the data as a byte
// // 	Read() uint8
// // }

// // // Transmit is a function that sends the data payload across to the device via the SPI line
// // type Transmit func(data ...byte)

// // const (
// // 	FullUpdate Mode = iota
// // 	PartialUpdate
// // )

// // // fullUpdate is a lookup table used whilst in full update mode
// // var fullUpdate = []byte{
// // 	0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00,
// // 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// // 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// // 	0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00,
// // 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// // }

// // // partialUpdate is a lookup table used whilst in partial update mode
// // var partialUpdate = []byte{
// // 	0x10, 0x18, 0x18, 0x08, 0x18, 0x18,
// // 	0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
// // 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// // 	0x00, 0x00, 0x13, 0x14, 0x44, 0x12,
// // 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// // }

// // // EPD defines the base type for the e-paper display driver
// // type EPD struct {
// // 	// dimensions of the display
// // 	Height int
// // 	Width  int

// // 	// pins used by this driver
// // 	rst  WriteablePin // for reset signal
// // 	dc   WriteablePin // for data/command select signal; D=HIGH C=LOW
// // 	cs   WriteablePin // for chip select signal; this pin is active low
// // 	busy ReadablePin  // for reading in busy signal

// // 	// SPI transmitter
// // 	transmit Transmit
// // }

// // // New creates a new EPD device driver
// // func New(rst, dc, cs WriteablePin, busy ReadablePin, transmit Transmit) *EPD {
// // 	return &EPD{296, 128, rst, dc, cs, busy, transmit}
// // }

// // // reset resets the display back to defaults
// // func (epd *EPD) reset() {
// // 	epd.rst.High()
// // 	time.Sleep(200 * time.Millisecond)
// // 	epd.rst.Low()
// // 	time.Sleep(10 * time.Millisecond)
// // 	epd.rst.High()
// // 	time.Sleep(200 * time.Millisecond)
// // }

// // // command transmits single byte of command instruction over the SPI line
// // func (epd *EPD) command(c byte) {
// // 	epd.dc.Low()
// // 	epd.cs.Low()
// // 	epd.transmit(c)
// // 	epd.cs.High()
// // }

// // // data transmits single byte of data payload over SPI line
// // func (epd *EPD) data(d byte) {
// // 	epd.dc.High()
// // 	epd.cs.Low()
// // 	epd.transmit(d)
// // 	epd.cs.High()
// // }

// // // idle reads from busy line and waits for the device to get into idle state
// // func (epd *EPD) idle() {
// // 	for epd.busy.Read() == 0x1 {
// // 		time.Sleep(200 * time.Millisecond)
// // 	}
// // }

// // // mode sets the device's mode (based on the LookupTable)
// // // The device can either be in FullUpdate mode where the whole display is updated each time an image is rendered
// // // or in PartialUpdate mode where only the changed section is updated (and it doesn't cause any flicker)
// // //
// // // Waveshare recommends doing full update of the display at least once per-day to prevent ghost image problems
// // func (epd *EPD) Mode(mode Mode) {
// // 	epd.reset()

// // 	// command+data below is taken from the python sample driver

// // 	// DRIVER_OUTPUT_CONTROL
// // 	epd.sendCommand(0x01)
// // 	epd.sendData(byte((epd.Height - 1) & 0xFF))
// // 	epd.sendData(byte(((epd.Height - 1) >> 8) & 0xFF))
// // 	epd.sendData(0x00)

// // 	// BOOSTER_SOFT_START_CONTROL
// // 	epd.sendCommand(0x0C)
// // 	epd.sendData(0xD7)
// // 	epd.sendData(0xD6)
// // 	epd.sendData(0x9D)

// // 	// WRITE_VCOM_REGISTER
// // 	epd.sendCommand(0x2C)
// // 	epd.sendData(0xA8)

// // 	// SET_DUMMY_LINE_PERIOD
// // 	epd.sendCommand(0x3A)
// // 	epd.sendData(0x1A)

// // 	// SET_GATE_TIME
// // 	epd.sendCommand(0x3B)
// // 	epd.sendData(0x08)

// // 	// DATA_ENTRY_MODE_SETTING
// // 	epd.sendCommand(0x11)
// // 	epd.sendData(0x03)

// // 	// WRITE_LUT_REGISTER
// // 	epd.sendCommand(0x32)
// // 	var lut = fullUpdate
// // 	if mode == PartialUpdate {
// // 		lut = partialUpdate
// // 	}
// // 	for _, b := range lut {
// // 		epd.sendData(b)
// // 	}
// // }

// // // Sleep puts the device into "deep sleep" mode where it draws zero (0) current
// // //
// // // Waveshare recommends putting the device in "deep sleep" mode (or disconnect from power)
// // // if doesn't need updating/refreshing.
// // func (epd *EPD) Sleep() {
// // 	epd.sendCommand(0x10)
// // 	epd.sendData(0x01)
// // }

// // // turnOnDisplay activates the display and renders the image that's there in the device's RAM
// // func (epd *EPD) turnOnDisplay() {
// // 	epd.sendCommand(0x22)
// // 	epd.sendData(0xC4)
// // 	epd.sendCommand(0x20)
// // 	epd.sendCommand(0xFF)
// // 	epd.idle()
// // }

// // // window sets the window plane used by device when drawing the image in the buffer
// // func (epd *EPD) window(x0, x1 byte, y0, y1 uint16) {
// // 	epd.sendCommand(0x44)
// // 	epd.sendData((x0 >> 3) & 0xFF)
// // 	epd.sendData((x1 >> 3) & 0xFF)

// // 	epd.sendCommand(0x45)
// // 	epd.sendData(byte(y0 & 0xFF))
// // 	epd.sendData(byte((y0 >> 8) & 0xFF))
// // 	epd.sendData(byte(y1 & 0xFF))
// // 	epd.sendData(byte((y1 >> 8) & 0xFF))
// // }

// // // cursor sets the cursor position in the device window frame
// // func (epd *EPD) cursor(x uint8, y uint16) {
// // 	epd.sendCommand(0x4E)
// // 	epd.sendData((x >> 3) & 0xFF)

// // 	epd.sendCommand(0x4F)
// // 	epd.sendData(byte(y & 0xFF))
// // 	epd.sendData(byte((y >> 8) & 0xFF))

// // 	epd.idle()
// // }

// // // Clear clears the display and paints the whole display into c color
// // func (epd *EPD) Clear(c color.Color) {
// // 	var img = image.White
// // 	if c != color.White {
// // 		img = image.Black // anything other than white is treated as black
// // 	}
// // 	_ = epd.Draw(img)
// // }

// // // Draw renders the given image onto the display
// // func (epd *EPD) Draw(img image.Image) error {
// // 	var isvertical = img.Bounds().Size().X == epd.Width && img.Bounds().Size().Y == epd.Height
// // 	var _, uniform = img.(*image.Uniform) // special case for uniform images which have infinite bound
// // 	if !uniform && !isvertical {
// // 		return ErrInvalidImageSize
// // 	}

// // 	epd.window(0, byte(epd.Width-1), 0, uint16(epd.Height-1))
// // 	for i := 0; i < epd.Height; i++ {
// // 		epd.cursor(0, uint16(i))
// // 		epd.sendCommand(0x24) // WRITE_RAM
// // 		for j := 0; j < epd.Width; j += 8 {
// // 			// this loop converts individual pixels into a single byte
// // 			// 8-pixels at a time and then sends that byte to render
// // 			var b = 0xFF
// // 			for px := 0; px < 8; px++ {
// // 				var pixel = img.At(j+px, i)
// // 				if isdark(pixel.RGBA()) {
// // 					b &= ^(0x80 >> (px % 8))
// // 				}
// // 			}
// // 			epd.sendData(byte(b))
// // 		}
// // 	}
// // 	epd.turnOnDisplay()
// // 	return nil
// // }

// // // isdark is a utility method which returns true if the pixel color is considered dark else false
// // // this function is taken from https://git.io/JviWg
// // func isdark(r, g, b, _ uint32) bool {
// // 	return math.Sqrt(
// // 		0.299*math.Pow(float64(r), 2)+
// // 			0.587*math.Pow(float64(g), 2)+
// // 			0.114*math.Pow(float64(b), 2)) <= 130
// // }
