
import math
import struct

from whatap.pack.pack import Pack

from whatap.value.null_value import NullValue

BYTE_MIN_VALUE = -128
BYTE_MAX_VALUE = 127
SHORT_MIN_VALUE = -32768
SHORT_MAX_VALUE = 32767
INT3_MIN_VALUE = -0x800000
INT3_MAX_VALUE = 0x007fffff
INT_MIN_VALUE = -0x80000000
INT_MAX_VALUE = 0x7fffffff
LONG5_MIN_VALUE = -0x8000000000
LONG5_MAX_VALUE = 0x0000007fffffffff
LONG_MIN_VALUE = -0x8000000000000000
LONG_MAX_VALUE = 0x7fffffffffffffff


class DataOutputX(object):
    def __init__(self):
        self.buffer = bytearray()

    @staticmethod
    def toInt(buf):
        return struct.unpack('>i', buf)[0]

    @staticmethod
    def toShort(buf):
        return struct.unpack('>h', buf)[0]

    @staticmethod
    def toLong6(buf):
        padded = b'\0\0' + buf
        return struct.unpack('>q', padded)[0]

    def writePack(self, v, ln_fmt):
        self.writeShort(v.getPackType())
        v.write(self)
        if ln_fmt:
            current_len = len(self.buffer)
            remainder = current_len % ln_fmt
            if remainder:
                pad_len = ln_fmt - remainder
                self.buffer.extend(b'\0' * pad_len)
        return self

    def writeStep(self, step):
        self.writeByte(step.getStepType())
        step.write(self)
        return self

    @classmethod
    def toBytes(cls, v, ln_fmt=None):
        if isinstance(v, Pack):
            return cls().writePack(v, ln_fmt).toByteArray()

        buf = bytearray()
        buf.extend(struct.pack('>i', v))

        return buf

    @classmethod
    def toBytesLong(cls, v):
        buf = bytearray()
        buf.extend(struct.pack('>q', v))
        return buf

    @classmethod
    def toBytesInt(cls, v):
        buf = bytearray()
        buf.extend(struct.pack('>i', v))
        return buf

    @classmethod
    def toIntArrInt(cls, v):
        return struct.pack('>' + 'I'*len(v), *v)

    def set(self, dest, pos, src):
        dest[pos:pos + len(src)] = src
        return dest

    def writeIntBytes(self, b):
        if not b or not len(b):
            self.writeInt(0)
        else:
            self.writeInt(len(b))
            self.write(b)
        return self

    def writeBoolean(self, v):
        self.buffer.extend(struct.pack('>?', v))
        return self

    def writeByte(self, v):
        self.buffer.extend(struct.pack('>B', v & 0xFF))
        return self

    def writeShort(self, v):
        self.buffer.extend(struct.pack('>H', v & 0xFFFF))
        return self

    def writeInt3(self, v):
        v1 = (v >> 16) & 0xFF
        v2 = (v >> 8) & 0xFF
        v3 = (v >> 0) & 0xFF
        self.buffer.extend(struct.pack('>BBB', v1, v2, v3))
        return self

    def writeInt(self, v):
        self.buffer.extend(struct.pack('>i', v))
        return self

    def writeLong5(self, v):
        v1 = ((v >> 32) & 0xFF)
        v2 = ((v >> 24) & 0xFF)
        v3 = ((v >> 16) & 0xFF)
        v4 = ((v >> 8) & 0xFF)
        v5 = ((v >> 0) & 0xFF)
        self.buffer.extend(struct.pack('>BBBBB', v1, v2, v3, v4, v5))
        return self

    def writeLong(self, v):
        self.buffer.extend(struct.pack('>Q', v & 0xFFFFFFFFFFFFFFFF))
        return self

    def writeFloat(self, v):
        self.buffer.extend(struct.pack('>f', v))
        return self

    def writeDouble(self, v):
        self.buffer.extend(struct.pack('>d', v))
        return self

    def writeDecimal(self, v):
        if v == 0:
            self.writeByte(0)
        elif BYTE_MIN_VALUE <= v <= BYTE_MAX_VALUE:
            self.writeByte(1)
            self.writeByte(v)
        elif SHORT_MIN_VALUE <= v <= SHORT_MAX_VALUE:
            self.writeByte(2)
            self.writeShort(v)
        elif INT3_MIN_VALUE <= v <= INT3_MAX_VALUE:
            self.writeByte(3)
            self.writeInt3(v)
        elif INT_MIN_VALUE <= v <= INT_MAX_VALUE:
            self.writeByte(4)
            self.writeInt(v)
        elif LONG5_MIN_VALUE <= v <= LONG5_MAX_VALUE:
            self.writeByte(5)
            self.writeLong5(v)
        elif LONG_MIN_VALUE <= v <= LONG_MAX_VALUE:
            self.writeByte(8)
            self.writeLong(v)

        return self

    def write(self, v):
        self.buffer.extend(v)
        return self

    def writeBlob(self, v):
        if not v:
            self.writeByte(0)
            return self
        ln = len(v)
        if ln <= 253:
            self.writeByte(ln)
            self.write(v)
        elif ln <= 65535:
            self.writeByte(255)
            self.writeShort(ln)
            self.write(v)
        else:
            self.writeByte(254)
            self.writeInt(ln)
            self.write(v)
        return self

    def writeText(self, v):
        if not v:
            self.writeByte(0)
        else:
            if isinstance(v, int):
                v = str(v)

            if isinstance(v, str):
                self.writeBlob(v.decode("utf-8", "replace").encode("utf-8"))
            else:
                self.writeBlob(v.encode("utf-8"))

        return self

    def writeUTF(self, v):
        v = v.decode("utf-8", "replace").encode("utf-8")
        if len(v) > 65535:
            v = v[:65535]

        self.buffer.extend(struct.pack('>H', len(v)))
        self.buffer.extend(v)
        return self

    def writeValue(self, v):
        if not v:
            v = NullValue()

        self.writeByte(v.getValueType())
        v.write(self)
        return self

    def toByteArray(self):
        return bytes(self.buffer)

    def flush(self):
        self.buffer.flush()

    def writeFloatArray(self, vv):
        if not vv:
            self.writeShort(0)
        else:
            self.writeShort(len(vv))
            for v in vv:
                self.writeFloat(v)
        return self

    def writeIntArray(self, vv):
        if not vv:
            self.writeShort(0)
        else:
            self.writeShort(len(vv))
            for v in vv:
                self.writeInt(v)
        return self

    def writeLongArray(self, vv):
        if not vv:
            self.writeShort(0)
        else:
            self.writeShort(len(vv))
            for v in vv:
                self.writeLong(v)
        return self

    def writeDecimalArray(self, vv):
        if not vv:
            self.writeShort(0)
        else:
            self.writeShort(len(vv))
            for v in vv:
                self.writeDecimal(v)
        return self

    def writeToPos(self, pos, v):
        new_bytes = struct.pack('>I', v & 0xFFFFFFFF)

        self.buffer[pos:pos+4] = new_bytes
        return self

    def size(self):
        return len(self.buffer)

def test():
    dout = DataOutputX()

    msg = dout.writeText("hello").writeText('test oname').writeInt(123456).toByteArray()
    print len(msg)

if __name__ == '__main__':
    test()
