#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Reader/Writer for plain binfiles.
"""

__version__ = "0.1.1"

__copyright__ = """
    objutils - Object file library for Python.

   (C) 2010-2019 by Christoph Schueler <cpu12.gems@googlemail.com>

   All Rights Reserved

  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.
"""

from contextlib import closing
import io
import zipfile

from objutils.section import Section
from objutils.image import Image
from objutils.utils import PYTHON_VERSION, create_string_buffer

##
## TODO: binzipped format: a separate file for each section + MANIFEST (csv: fname, address, length)
##

class NoContiniousError(Exception): pass


class Reader(object):

    def load(self, fp, address = 0x0000):
        if isinstance(fp, str):
            fp = open(fp, "rb")
        data = fp.read()
        sec = Section(address, data)
        img = Image([sec], valid = True)
        if hasattr(fp, "close"):
            fp.close()
        return img

    def loads(self, image, address = 0x0000):
        if PYTHON_VERSION.major == 3:
            if isinstance(image, str):
                return self.load(create_string_buffer(bytes(image, "ascii")), address)
            else:
                return self.load(create_string_buffer(image), address)
        else:
            return self.load(create_string_buffer(image), address)


class Writer(object):
    def dump(self, fp, image, filler = b'\xff', **kws):
        if isinstance(fp, str):
            fp = open(fp, "wb")
        fp.write(self.dumps(image, filler))
        if hasattr(fp, "close"):
            fp.close()

    def dumps(self, image, filler = b'\xff', **kws):
        if not isinstance(filler, (bytes, int)):
            raise TypeError("filler must be of type 'bytes' or 'int'")
        if isinstance(filler, bytes) and len(filler) > 1:
            raise TypeError("filler must be a single byte")
        elif isinstance(filler, int) and filler > 255:
            raise ValueError("filler must be in range 0..255")
        result = bytearray()
        previous_address = None
        previous_length = None

        if hasattr(image, "sections") and  not image.sections:
            return b''
        sections = sorted(image.sections, key = lambda x: x.start_address)
        for section in sections:
            if not previous_address is None:
                gap = section.start_address - (previous_address + previous_length)
                if gap > 0:
                    result.extend(filler * gap)
            result.extend(section.data)
            previous_address = section.start_address
            previous_length = section.length
        return result

#

class BinZipReader(object):
    pass


class BinZipWriter(object):

    SECTION_FILE_NAME = "image{0:d}.bin"
    MANIFEST_FILE_NAME = "IMAGES.mf"

    def dump(self, fp, image, **kws):
        fp.write(self.dumps(image))
        if hasattr(fp, "close"):
            fp.close()

    def dumps(self, image, **kws):

        if hasattr(image, "sections") and  not image.sections:
            return b''
        sections = sorted(image.sections, key = lambda x: x.start_address)
        manifest_buffer = io.StringIO()
        out_buffer = io.BytesIO()
        #out_buffer = io.StringIO()
        print("BUF", out_buffer)
        with closing(zipfile.ZipFile(out_buffer, mode = "w")) as outFile:
            print(outFile)
            for idx, section in enumerate(sections):
                print(section.start_address, section.length)
                #print("FN", BinZipWriter.SECTION_FILE_NAME.format(idx))
                manifest_buffer.write(BinZipWriter.SECTION_FILE_NAME.format(idx))
                manifest_buffer.write("\t")
                manifest_buffer.write(str(section.start_address))
                manifest_buffer.write("\t")
                manifest_buffer.write(str(section.length))
                manifest_buffer.write("\n")
        manifest_buffer.seek(0)
        print(manifest_buffer.read())
        return ''
