from filetypes.base import *
import malcat
import struct
from filetypes.FAT12 import Fat12Analyzer, BootSector, DirectoryEntry

# https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system


class Fat16Analyzer(Fat12Analyzer):
    category = malcat.FileType.FILESYSTEM
    name = "FAT16"
    regexp = r"\xEB.\x90.{8}\x00\x02[\x01\x02\x04\x08\x10\x20\x40\x80]..\x02....\xf8...{14}\x29.{15}FAT16   .{448}\x55\xaa"


    def iter_cluster_chain(self, cluster):
        seen = set()
        while cluster >= 2 and cluster <= 0xFFEF and cluster not in seen:
            seen.add(cluster)
            yield cluster
            if cluster >= len(self.fat):
                raise FatalError("No FAT entry for cluster {:x}".format(cluster))
            cluster = self.fat[cluster]
    
    def parse(self, hint):
        boot_sector = yield BootSector()
        ebp = boot_sector["ExtendedBiosParameter"]
        self.sector_size = ebp["BytesPerSector"]
        self.cluster_size = ebp["SectorsPerCluster"] * self.sector_size
        self.reserved_space = ebp["ReservedSectors"] * self.sector_size
        self.add_section("Boot", 0, self.reserved_space, x=True)
        eof = boot_sector["ExtendedBiosParameter"]["TotalLogicalSectors"] * self.sector_size
        self.set_eof(eof)

        # read FATs
        self.jump(self.reserved_space)
        fat_size = ebp["SectorsPerFat"] * self.sector_size
        self.add_section("FAT", self.reserved_space, ebp["NumberOfFAT"] * fat_size, discardable=True)
        for i in range(ebp["NumberOfFAT"]):
            fat = yield Array(UInt16(), name="FAT#{}".format(i), category=Type.FIXUP)
            if not self.fat:
                self.fat = [x.value for x in fat]
        self.confirm()

        # read root dir
        if not ebp["MaximumNumberOfRootDirectoryEntries"]:
            raise FatalError("Not a valid root dir")
        num_entries = ebp["MaximumNumberOfRootDirectoryEntries"]
        self.user_space = self.tell() + num_entries * 32
        self.add_section("RootDir", self.tell(), num_entries * 32)
        self.root = yield from self.parse_dir_range(self.tell(), num_entries*32)

        self.add_section("Clusters", self.user_space, eof - self.user_space, r=True, w=True)
