/*
 * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package jace.hardware.massStorage;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * Representation of a prodos file with a known file type and having a known
 * size (either seedling, sapling or tree)
 *
 * @author Brendan Robert (BLuRry) brendan.robert@gmail.com 
 */
public class FileNode extends DiskNode {

    public enum FileType {

        UNKNOWN(0x00, 0x0000),
        ADB(0x019, 0x0000),
        AWP(0x01a, 0x0000),
        ASP(0x01b, 0x0000),
        BAD(0x01, 0x0000),
        BIN(0x06, 0x0300),
        CLASS(0xED, 0x0000),
        BAS(0xfc, 0x0801),
        CMD(0x0f0, 0x0000),
        INT(0xfa, 0x0801),
        IVR(0xfb, 0x0000),
        PAS(0xef, 0x0000),
        REL(0x0Fe, 0x0000),
        SHK(0x0e0, 0x08002),
        SDK(0x0e0, 0x08002),
        SYS(0x0ff, 0x02000),
        SYSTEM(0x0ff, 0x02000),
        TXT(0x04, 0x0000),
        U01(0x0f1, 0x0000),
        U02(0x0f2, 0x0000),
        U03(0x0f3, 0x0000),
        U04(0x0f4, 0x0000),
        U05(0x0f5, 0x0000),
        U06(0x0f6, 0x0000),
        U07(0x0f7, 0x0000),
        U08(0x0f8, 0x0000),
        VAR(0x0FD, 0x0000);
        public int code = 0;
        public int defaultLoadAddress = 0;

        FileType(int code, int addr) {
            this.code = code;
            this.defaultLoadAddress = addr;
        }
    }
    public int fileType = 0x00;
    public int loadAddress = 0x00;
    public static int SEEDLING_MAX_SIZE = ProdosVirtualDisk.BLOCK_SIZE;
    public static int SAPLING_MAX_SIZE = ProdosVirtualDisk.BLOCK_SIZE * 128;

    @Override
    public EntryType getType() {
        long fileSize = getPhysicalFile().length();
        if (fileSize <= SEEDLING_MAX_SIZE) {
            setType(EntryType.SEEDLING);
            return EntryType.SEEDLING;
        } else if (fileSize <= SAPLING_MAX_SIZE) {
            setType(EntryType.SAPLING);
            return EntryType.SAPLING;
        }
        setType(EntryType.TREE);
        return EntryType.TREE;
    }

    @Override
    public void setName(String name) {
        String[] parts = name.split("\\.");
        FileType t = null;
        int offset = 0;
        if (parts.length > 1) {
            String extension = parts[parts.length - 1].toUpperCase();
            String[] extParts = extension.split("#");
            if (extParts.length == 2) {
                offset = Integer.parseInt(extParts[1], 16);
                extension = extParts[0];
            }
            try {
                t = FileType.valueOf(extension);
            } catch (IllegalArgumentException ex) {
                System.out.println("Not sure what extension " + extension + " is!");
            }
            name = "";
            for (int i = 0; i < parts.length - 1; i++) {
                name += (i > 0 ? "." + parts[i] : parts[i]);
            }
            if (extParts[extParts.length - 1].equals("SYSTEM")) {
                name += ".SYSTEM";
            }
        }
        if (t == null) {
            t = FileType.UNKNOWN;
        }
        if (offset == 0) {
            offset = t.defaultLoadAddress;
        }
        fileType = t.code;
        loadAddress = offset;

        // Pass usable name (stripped of file extension and other type info) as name
        super.setName(name);
    }

    public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException {
        setOwnerFilesystem(ownerFilesystem);
        setPhysicalFile(file);
    }

    @Override
    public void doDeallocate() {
    }

    @Override
    public void doAllocate() throws IOException {
        int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
        int treeBlocks;
        if (dataBlocks > 1 && dataBlocks < 257) {
            treeBlocks = 1;
        } else {
            treeBlocks = 1 + (dataBlocks / 256);
        }
        for (int i = 1; i < dataBlocks + treeBlocks; i++) {
            SubNode subNode = new SubNode(i, this);
        }
    }

    @Override
    public void doRefresh() {
    }

    @Override
    public void readBlock(int block, byte[] buffer) throws IOException {
//        System.out.println("Read block "+block+" of file "+getName());
        switch (this.getType()) {
            case SEEDLING:
                readFile(buffer, 0);
                break;
            case SAPLING:
                if (block > 0) {
                    readFile(buffer, (block - 1));
                } else {
                    // Generate seedling index block
                    generateIndex(buffer, 0, 256);
                }
                break;
            case TREE:
                int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
                int treeBlocks = (dataBlocks / 256);
                if (block == 0) {
                    generateIndex(buffer, 0, treeBlocks);
                } else if (block < treeBlocks) {
                    int start = treeBlocks + (block - 1 * 256);
                    int end = Math.min(start + 256, treeBlocks);
                    generateIndex(buffer, treeBlocks, end);
                } else {
                    readFile(buffer, (block - treeBlocks));
                }
                break;
        }
    }

    private void readFile(byte[] buffer, int start) throws IOException {
        try (FileInputStream f = new FileInputStream(physicalFile)) {
            f.skip(start * ProdosVirtualDisk.BLOCK_SIZE);
            f.read(buffer, 0, ProdosVirtualDisk.BLOCK_SIZE);
        }
    }

    private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
        int pos = 0;
        for (int i = indexStart; pos < 256 && i < indexLimit && i < additionalNodes.size(); i++, pos++) {
            buffer[pos] = (byte) (additionalNodes.get(i).baseBlock & 0x0ff);
            buffer[pos + 256] = (byte) ((additionalNodes.get(i).baseBlock >> 8) & 0x0ff);
        }
    }
}
