"""
------------------------------------------------------------------------------
--                                                                          --
--                                 LAB 10                                   --
--                                                                          --
--                               F F 7 . P Y                                --
--                                                                          --
------------------------------------------------------------------------------
-- <Your Name Here>                                                         --
--                                                                          --
-- CISC106 011 Spring 2013                                                  --
--                                                                          --
------------------------------------------------------------------------------
-- 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 str(save)

class SaveFile():
    def __init__(self, fin):
        """
        Constructs a SaveFile by reading it in from open file fin.
        """
        try:
            if fin.closed or fin.mode != 'rb':
                raise IOError('Save file %s must be opened for reading in binary mode' % fin.name)
        except AttributeError:
            raise IOError('Save file must be a valid file')

        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 __str__(self):
        """
        This __str__ 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
