/*
 * Copyright (C) 2008-2009 KenD00
 * 
 * This file is part of DumpHD.
 * 
 * DumpHD 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 3 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, see <http://www.gnu.org/licenses/>.
 */
package dumphd.bdplus;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import dumphd.util.ByteArray;
import dumphd.util.ByteSource;
import dumphd.util.Utils;

/**
 * This class represents a BD+ conversion table.
 * 
 * TODO: With an ArraySource class the doubled code in parse wouldn't be necessary
 * 
 * @author KenD00
 */
public class ConversionTable {

   /**
    * All subtables of the conversion table, in the order as they appear in the conversion table
    */
   private ArrayList<SubTable> tables = new ArrayList<SubTable>();
   /**
    * Index map, maps the subtable id to it's index inside the tables ArrayList
    */
   private HashMap<Long, Integer> indexTable = new HashMap<Long, Integer>(128);


   /**
    * Creates an empty ConversionTable.
    */
   public ConversionTable() {
      // Nothing to do
   }
   
   /**
    * Creates a ConversionTable from the given source.
    * 
    * The conversion table must start at position 0 and the read pointer must be located there.
    * 
    * @param source The conversion table to parse
    * @throws IOException
    */
   public ConversionTable(ByteSource source) throws IOException {
      if (source.read(Utils.buffer, 0, 2) != 2) {
         throw new IOException("Unexpected EOF, conversion table too small for header");
      }
      int tableCount = ByteArray.getUShort(Utils.buffer, 0);
      tables.ensureCapacity(tableCount);
      for (int i = 0; i < tableCount; i++) {
         SubTable subTable = new SubTable(source);
         tables.add(subTable);
         indexTable.put(subTable.getTableId(), i);
      }
   }
   
   /**
    * Parses the given byte array to create a ConversionTable.
    * 
    * The content of the byte array gets copied, changes of it after this method call do not affect this ConversionTable. 
    * 
    * @param raw The raw Conversion Table
    * @param offset Start offset, must be 0 or metadata will get wrong
    * @return The endoffset of the Conversion Table inside the byte array
    * @throws IndexOutOfBoundsException
    */
   public int parse(byte[] raw, int offset) throws IndexOutOfBoundsException {
      int tableCount = ByteArray.getUShort(raw, offset);
      offset += 2;
      tables.ensureCapacity(tableCount);
      for (int i = 0; i < tableCount; i++) {
         SubTable subTable = new SubTable();
         offset = subTable.parse(raw, offset);
         tables.add(subTable);
         indexTable.put(subTable.getTableId(), i);
      }
      return offset;
   }

   /**
    * @return The number of subtables
    */
   public int size() {
      return tables.size();
   }

   /**
    * Returns the subtable with the given table id, or null if its not present.
    * 
    * @param tableId Table id of the requested subtable
    * @return The requested subtable or null if its not present
    */
   public SubTable getSubTable(long tableId) {
      Integer index = indexTable.get(tableId);
      if (index != null) {
         return tables.get(index);
      } else {
         return null;
      }
   }

   /**
    * @return An iterator over the subtables, in the order as they appear in the source
    */
   public Iterator<SubTable> iterator() {
      return tables.iterator();
   }

}
