/*
 * Copyright (C) 2012.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * 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.
 */
package uk.me.parabola.imgfmt.app.mdr;

import java.text.Collator;
import java.util.ArrayList;
import java.util.List;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.srt.Sort.SrtCollator;

/**
 * Holds an index of name prefixes to record numbers.
 *
 * Extends MdrSection, although is sometimes a subsection, not an actual section.
 *
 * @author Steve Ratcliffe
 */
public class PrefixIndex extends MdrSection {
	private final int prefixLength;
	private int maxIndex;

	// We use mdr8record for all similar indexes.
	private final List<Mdr8Record> index = new ArrayList<>();

	/**
	 * Sets the config and the prefix length for this index.
	 *
	 * Prefix length may differ depending on the amount of data, so will have
	 * to deal with that when it happens.
	 *
	 * @param config Configuration for sorting methods.
	 * @param prefixLength The prefix length for this index.
	 */
	public PrefixIndex(MdrConfig config, int prefixLength) {
		setConfig(config);
		this.prefixLength = prefixLength;
	}

	/**
	 * We can create an index for any type that has a name.
	 * @param list A list of items that have a name.
	 * @param grouped used with MDR7 records  
	 */
	public void createFromList(List<? extends NamedRecord> list, boolean grouped) {
		maxIndex = list.size();

		// Prefixes are equal based on the primary unaccented character, so
		// we need to use the collator to test for equality and not equals().
		Sort sort = getConfig().getSort();
		Sort.SrtCollator collator = (SrtCollator) sort.getCollator();
		collator.setStrength(Collator.PRIMARY);

		String lastCountryName = null;
		char[] lastPrefix = "".toCharArray();
		int inRecord = 0;  // record number of the input list
		int outRecord = 0; // record number of the index
		int lastMdr22SortPos = -1; 
		for (NamedRecord r : list) {
			inRecord++;
			String name;
			if (r instanceof Mdr7Record) {
				name = ((Mdr7Record) r).getPartialName();
				if (grouped) {
					int mdr22SortPos = ((Mdr7Record) r).getCity().getMdr22SortPos();
					if (mdr22SortPos != lastMdr22SortPos)
						lastPrefix = "".toCharArray();
					lastMdr22SortPos = mdr22SortPos; 
				}
			}
			else 
				name = r.getName();
			char[] prefix = sort.getPrefix(name, prefixLength);
			int cmp = collator.compareOneStrengthWithLength(prefix, lastPrefix, Collator.PRIMARY, prefixLength);
			if (cmp > 0) {
				outRecord++;
				index.add(new Mdr8Record(prefix, inRecord));

				lastPrefix = prefix;
				
				if (grouped) {
					// Peek into the real type to support the mdr17 feature of indexes sorted on country.
					Mdr5Record city = ((Mdr7Record) r).getCity();
					if (city != null) {
						String countryName = city.getCountryName();
						if (!countryName.equals(lastCountryName)) {
							city.getMdrCountry().getMdr29().setMdr17(outRecord);
							lastCountryName = countryName;
						}
					}
				}
			}
		}
	}
	
	/**
	 * We can create an index for any type that has a name.
	 * @param list A list of items that have a name.
	 */
	public void createFromList(List<? extends NamedRecord> list) {
		createFromList(list, false);
	}

	/**
	 * Write the section or subsection.
	 */
	public void writeSectData(ImgFileWriter writer) {
		int size = Utils.numberToPointerSize(maxIndex);
		for (Mdr8Record s : index) {
			for (int i = 0; i< prefixLength; i++) {
				writer.put1u(s.getPrefix()[i]);
			}
			writer.putNu(size, s.getRecordNumber());
		}
	}

	public int getItemSize() {
		return prefixLength + Utils.numberToPointerSize(maxIndex);
	}

	protected int numberOfItems() {
		return index.size();
	}

	public int getPrefixLength() {
		return prefixLength;
	}
}
