# -*- coding: utf8 -*-

"""
Copyright (C) 2007 Adolfo González Blázquez <code@infinicode.org>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version. 

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. 

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

If you find any bugs or have any suggestions email: code@infinicode.org
"""

import struct

class Message:

        def __init__(self):

                NATIVE = '@'
                BIG_ENDIAN = '>'
                LITTLE_ENDIAN = '<'
                NETWORK = '!'

                self._order = LITTLE_ENDIAN
                self.size = 0
                self.code = 0
                self.data = ''

        def get_name(self):

                values = {
                        0: "CoreProtocol",
                        1: "OptionsInfo",
                        3: "DefineSearches",
                        4: "ResultInfo",
                        5: "SearchResult",
                        9: "FileUpdateAvailability",
                        10: "FileAddSource",
                        12: "ServerUser",
                        13: "ServerState",
                        15: "ClientInfo",
                        16: "ClientState",
                        19: "ConsoleMessage",
                        20: "NetworkInfo",
                        21: "UserInfo",
                        22: "RoomInfo",
                        23: "RoomMessage",
                        24: "RoomAddUser",
                        26: "ServerInfo",
                        27: "MessageFromClient",
                        28: "ConnectedServers",
                        31: "RoomInfo",
                        34: "SharedFileUpload",
                        35: "SharedFileUnshared",
                        36: "AddSectionOption",
                        38: "AddPluginOption",
                        46: "FileDownloadUpdate",
                        47: "BadPassword",
                        33: "SharedFileInfo",
                        48: "SharedFileInfo",
                        25: "ClientStats",
                        37: "ClientStats",
                        39: "ClientStats",
                        49: "ClientStats",
                        50: "FileRemoveSource",
                        51: "CleanTables",
                        7: "FileInfo",
                        40: "FileInfo",
                        43: "FileInfo",
                        52: "FileInfo",
                        29: "DownloadingFiles",
                        41: "DownloadingFiles",
                        44: "DownloadingFiles",
                        53: "DownloadingFiles",
                        30: "DownloadingFiles",
                        42: "DownloadingFiles",
                        45: "DownloadingFiles",
                        54: "DownloadedFiles",
                        55: "Uploaders",
                        56: "Pending",
                        57: "Search",
                        58: "Version"
                }

                if self.code in values:
                        return values[self.code]
                else:
                        return "Unknown"

class MessageSender(Message):

        def __init__(self, socket, code):
                Message.__init__(self)
                self.size = 2                   # Code size is included
                self.code = code
                self.data = ''
                self.socket = socket

        def send(self):
                size = struct.pack(self._order+'i', self.size)
                code = struct.pack(self._order+'h', self.code)
                self.socket.send(size + code + self.data)

        def int8(self, value):
                self.size += 1
                self.data += struct.pack('b', value)

        def uint8(self, value):
                self.size += 1
                self.data += struct.pack('B', value)

        def int16(self, value):
                self.size += 2
                self.data += struct.pack(self._order+'h', value)

        def uint16(self, value):
                self.size += 2
                self.data += struct.pack(self._order+'H', value)

        def int32(self, value):
                self.size += 4
                self.data += struct.pack(self._order+'i', value)

        def uint32(self, value):
                self.size += 4
                self.data += struct.pack(self._order+'I', value)

        def int64(self, value):
                self.size += 8
                self.data += struct.pack(self._order+'q', value)

        def uint64(self, value):
                self.size += 8
                self.data += struct.pack(self._order+'Q', value)

        def char(self, value):
                self.size += 1
                self.data += struct.pack(self._order+'c', value)

        def string(self, value):
                length = len(value)
                self.int16(length)
                for i in range(length):
                        self.char(value[i])

class MessageReader(Message):

        def __init__(self, socket):
                Message.__init__(self)
                self.socket = socket
                self.index = 0

                self.read_size()
                self.read_code()
                if self.size - 2 > 0:
                        self.read_data()

        def read_random(self, size):
                return self.socket.recv(size)

        def read_data(self):
                self.data = self.socket.recv(self.size - 2)

        def read_size(self):
                self.size = struct.unpack(self._order+'i', self.socket.recv(4))[0]

        def read_code(self):
                self.code = struct.unpack(self._order+'h', self.socket.recv(2))[0]

        def int8(self):
                data = struct.unpack('b', self.data[self.index:self.index+1])[0]
                self.index += 1
                return data

        def uint8(self):
                data = struct.unpack('B', self.data[self.index:self.index+1])[0]
                self.index += 1
                return data

        def int16(self):
                data = struct.unpack(self._order+'h', self.data[self.index:self.index+2])[0]
                self.index += 2
                return data

        def uint16(self):
                data = struct.unpack(self._order+'H', self.data[self.index:self.index+2])[0]
                self.index += 2
                return data

        def int32(self):
                data = struct.unpack(self._order+'i', self.data[self.index:self.index+4])[0]
                self.index += 4
                return data

        def uint32(self):
                data = struct.unpack(self._order+'I', self.data[self.index:self.index+4])[0]
                self.index += 4
                return data

        def int64(self):
                data = struct.unpack(self._order+'q', self.data[self.index:self.index+8])[0]
                self.index += 8
                return data

        def uint64(self):
                data = struct.unpack(self._order+'Q', self.data[self.index:self.index+8])[0]
                self.index += 8
                return data

        def float(self):
                length = self.int16()
                data = ''
                for i in range(length):
                        data += self.char()
                return data

        def char(self):
                data = struct.unpack('c', self.data[self.index:self.index+1])[0]
                self.index += 1
                return data

        def chararray(self, size):
                val = ''
                for i in range(size):
                        val += self.char()
                return val

        def string(self):
                s = ''
                length = self.int16()
                for i in range(length):
                        s += self.char()
                return s

        """
        def list(self, type1, type2=None):
                length = self.int16()
                l = []
                print "list of", length
                for i in range(length):
                        v1 = getattr(self, type1)()
                        if type2 != None:
                                v2 = getattr(self, type2)()
                                l.append([v1,v2])
                        else:
                                l.append(v1)
                return l
        """
        def list(self, elems):
                length = self.int16()
                print " List of", length, "with", elems
                l = []
                if not isinstance(elems, list):
                        for i in range(length):
                                v = getattr(self, elems)()
                                l.append(v)
                else:
                        for i in range(length):
                                ll = []
                                for t in range(len(elems)):
                                        v = getattr(self, elems[t])()
                                        ll.append(v)
                                l.append(l)
                return l