"""
------------------------------------------------------------------------------
--                                                                          --
--                                 LAB 07                                   --
--                                                                          --
--                             L A B 0 7 . P Y                              --
--                                                                          --
------------------------------------------------------------------------------
-- <Your Name Here>                                                         --
--                                                                          --
-- <Class Name Here>                                                        --
--                                                                          --
------------------------------------------------------------------------------
-- This file contains code to get information out of a Final Fantasy VII    --
-- save file (which is a binary file) and return that information in a      --
-- string format.                                                           --
--                                                                          --
------------------------------------------------------------------------------
"""

import struct

def read_save_string(fin):
    """
    takes an open binary read file fin which should be a Final Fantasy VII
    save file and reads in ONE save game from the file.  It will then return
    the string representation of that file.
    """
    save = SaveFile(fin)
    return unicode(save)

class SaveFile():
    def __init__(self, fin):
        """
        Constructs a SaveFile by reading it in from open file fin.
        """
        if type(fin) != file:
            raise IOError, 'Save file must be a valid file'

        if fin.closed or fin.mode != 'rb':
            raise IOError, 'Save file %s must be opened for reading in binary mode' % fin.name

        fin.read(13)

        try:
            self.level = struct.unpack('B', fin.read(1))[0]
        except struct.error:
            raise IOError, 'Reached end of save file %s' % fin.name

        if self.level == 0:
            fin.seek(-14, 1)
            raise IOError, 'Reached end of save file %s' % fin.name

        self.characters = []
        for i in range(3):
            char_byte = struct.unpack('B', fin.read(1))[0]

            if char_byte == 0:
                self.characters.append('Cloud')
            elif char_byte == 1:
                self.characters.append('Barret')
            elif char_byte == 2:
                self.characters.append('Tifa')
            elif char_byte == 3:
                self.characters.append('Aerith')
            elif char_byte == 4:
                self.characters.append('Red XII')
            elif char_byte == 5:
                self.characters.append('Yuffie')
            elif char_byte == 6:
                self.characters.append('Cait Sith')
            elif char_byte == 7:
                self.characters.append('Vincent')
            elif char_byte == 8:
                self.characters.append('Cid')
            elif char_byte == 255:
                pass
            else:
                raise IOError, 'Save file %s is corrupt' % fin.name

        name_bytes = []
        for i in range(16):
            name_bytes.append(struct.unpack('B', fin.read(1))[0])

        try:
            name_bytes.index(255)
            name_bytes = name_bytes[:name_bytes.index(255)]
        except ValueError:
            pass
        self.name = ''.join(chr(i + 32) for i in name_bytes)

        self.hp = (
            struct.unpack('H', fin.read(2))[0], 
            struct.unpack('H', fin.read(2))[0])

        self.mp = (
            struct.unpack('H', fin.read(2))[0], 
            struct.unpack('H', fin.read(2))[0])

        self.gil = struct.unpack('I', fin.read(4))[0]
        fin.read(4)

        location_bytes = []
        for i in range(32):
            location_bytes.append(struct.unpack('B', fin.read(1))[0])

        try:
            location_bytes.index(255)
            location_bytes = location_bytes[:location_bytes.index(255)]
        except ValueError:
            pass
        self.location = ''.join(chr(i + 32) for i in location_bytes)
        fin.read(4259)

    def __unicode__(self):
        """
        This __unicode__ returns a string containing only overview information:
        the name of the leading character, level, HP and MP of said character,
        the characters in the party, amount of money held by the party and
        location of the party.
        """
        s = 'Level : %d' % self.level
        s += ';Name : ' + self.name
        s += ';HP : %d/%d' % self.hp
        s += ';MP : %d/%d' % self.mp
        s += ';Characters : ' + self.characters[0]
        if len(self.characters) > 1:
            s += ', ' + self.characters[1]
        if len(self.characters) == 3:
            s += ' & ' + self.characters[2]
        s += ';Gil : %d' % self.gil
        s += ';Location : "%s"' % self.location

        return s

    def __str__(self):
        """
        Simply ensures that __unicode__ is called upon casting to an ASCII
        string
        """
        return self.__unicode__()
