package yuku.alkitab.yes2;

import android.util.Log;

import java.util.ArrayList;
import java.util.List;

import yuku.alkitab.yes2.io.RandomOutputStream;
import yuku.alkitab.yes2.section.base.SectionContent;
import yuku.bintex.BintexWriter;
import yuku.bintex.ValueMap;

/**
 * YES version 2 file format
 * 
 * Note: Below, uint8, char8, and byte are the same thing, only interpreted differently.
 * Note: value is Bintex value type
 * Note: int is 32-bit integer
 * 
 * {
 * | uint8[8] header = 0x98 0x58 0x0d 0x0a 0x00 0x5d 0xe0 0x02 // version 2
 * | int sectionIndex.size
 * | uint8 sectionIndexVersion = 1
 * | int section_count
 * | {
 * | | uint8 sectionName.length
 * | | char8[sectionName.length] sectionName
 * | | int offset // from start of sections
 * | | int attributes_size
 * | | int content_size
 * | | byte[4] reserved
 * | }[section_count] sectionIndex
 * | {
 * | | value section.attributes
 * | | byte[section.content.size] section.content
 * | }[section_count] sections
 * | uint8 footer = 0
 * }
 */
public class Yes2Writer {
	private static final String TAG = Yes2Writer.class.getSimpleName();

	private static final byte YES_VERSION = 0x02;
	private static final byte[] YES_HEADER = { (byte) 0x98, 0x58, 0x0d, 0x0a, 0x00, 0x5d, (byte) 0xe0, YES_VERSION };

	private static Boolean androidLogPossible = null;

	private static void alog(long pos, String msg) {
		alog("[pos=" + pos + "] " + msg, null);
	}

	private static void alog(String msg, Throwable t) {
		if (androidLogPossible == null) {
			try {
				Class.forName("android.util.Log");
				androidLogPossible = true;
			} catch (Exception e) {
				androidLogPossible = false;
			}
		}

		if (androidLogPossible) {
			if (t != null) {
				Log.e(TAG, msg, t);
			} else {
				Log.d(TAG, msg);
			}
		} else {
			System.err.println(msg);
			if (t != null) {
				t.printStackTrace();
			}
		}
	}

	public List<SectionContent> sections = new ArrayList<SectionContent>();

	public void writeToFile(RandomOutputStream output) throws Exception {
		BintexWriter bw = new BintexWriter(output);
		
		int section_count = sections.size();
		
		////////// HEADERS //////////
		
		alog(output.getFilePointer(), "write yes header");
		bw.writeRaw(YES_HEADER);
		
		///////// SECTION INDEX ///////////
		
		long savedpos_sectionIndexSize = -1;
		long savedpos_startOfSectionIndex = -1;
		long[] savedpos_sectionIndexSizeEntries = new long[section_count];
		long savedpos_startOfSections = -1;
		int[] saved_sectionOffset = new int[section_count];
		int[] savedsizes_sectionAttributes = new int[section_count];
		int[] savedsizes_sectionContent = new int[section_count];
		
		{ // section index size is not known, just save position and reserve bytes
			savedpos_sectionIndexSize = output.getFilePointer();
			bw.writeInt(-1); // for sectionIndex size
		}
		
		alog(output.getFilePointer(), "write sectionIndexVersion: 1");
		bw.writeUint8(1);
		
		alog(output.getFilePointer(), "write section_count: " + section_count);
		bw.writeInt(section_count);
		
		savedpos_startOfSectionIndex = output.getFilePointer();
		
		for (int i = 0; i < section_count; i++) {
			SectionContent section = sections.get(i);
			
			{ // section name
				String name = section.getName();
				byte[] name_bytes = section.getNameAsBytesWithLength();
				alog(output.getFilePointer(), "write sectionName: " + name);
				bw.writeRaw(name_bytes);
			}
			
			{ // sizes are not known, just save position and reserve bytes
				savedpos_sectionIndexSizeEntries[i] = output.getFilePointer();
				bw.writeInt(-1); // for offset
				bw.writeInt(-1); // for attributes_size
				bw.writeInt(-1); // for content_size
				bw.writeInt(0); // for reserved
			}
		}
		
		{ // now we know the size of the section index, write it into the placeholder
			long lastPos = output.getFilePointer();
			output.seek(savedpos_sectionIndexSize);
			int size = (int) (lastPos - savedpos_startOfSectionIndex);
			alog(output.getFilePointer(), "write section index size: " + size);
			bw.writeInt(size);
			output.seek(lastPos);
		}
		
		//////////////// SECTION ATTRIBUTES AND CONTENT /////////////////
		
		savedpos_startOfSections = output.getFilePointer();
		
		for (int i = 0; i < section_count; i++) {
			SectionContent section = sections.get(i);
			String section_name = section.getName();
			
			saved_sectionOffset[i] = (int) (output.getFilePointer() - savedpos_startOfSections);

			{ // attributes
				int posBeforeAttributes = (int) output.getFilePointer();
				alog(output.getFilePointer(), "write section attributes: " + section_name);
				ValueMap attributes = section.getAttributes();
				bw.writeValueSimpleMap(attributes == null? new ValueMap(): attributes);
				savedsizes_sectionAttributes[i] = (int) output.getFilePointer() - posBeforeAttributes;
			}
			
			{ // contents
				int posBeforeContent = (int) output.getFilePointer();
				alog(output.getFilePointer(), "write section content: " + section_name);
				((SectionContent.Writer) section).write(output);
				savedsizes_sectionContent[i] = (int) output.getFilePointer() - posBeforeContent;
			}
		}
		
		//////////// WRITE INTO PLACEHOLDERS ////////////////
		
		long lastPos = output.getFilePointer();

		{ // offsets and sizes in the section index
			for (int i = 0; i < section_count; i++) {
				SectionContent section = sections.get(i);
				String section_name = section.getName();
				
				int offset = saved_sectionOffset[i];
				int attributes_size = savedsizes_sectionAttributes[i];
				int content_size = savedsizes_sectionContent[i];
				
				output.seek(savedpos_sectionIndexSizeEntries[i]);
				alog(output.getFilePointer(), "write offset, attributes_size, content_size of section " + section_name + ": " + offset + ", " + attributes_size + ", " + content_size); 
				bw.writeInt(offset);
				bw.writeInt(attributes_size);
				bw.writeInt(content_size);
			}
		}
		
		output.seek(lastPos);

		alog(output.getFilePointer(), "write footer");
		bw.writeUint8(0);
		bw.close();
		
		alog(output.getFilePointer(), "done");
	}

}

@@ -5956,22 +5956,8 @@
 0);%0A
-%09%09bw.close();%0A
 %09%09%0A%09
@@ -5996,14 +5996,28 @@
 done%22);%0A
+%09%09bw.close();%0A
 %09%7D%0A%0A%7D%0A

Fix bug in writing yes2 file