/*******************************************************************************
 * Copyright (c) 2007, 2008 IBM Corporation
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 ******************************************************************************/
package org.eclipse.cosmos.me.internal.deployment.sdd.tooling.btg.rpm;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.Hashtable;

import org.eclipse.cosmos.me.internal.deployment.sdd.tooling.btg.rpm.util.HexUtils;


public class RPMFile {
	public static String LEAD_VERSION = "LEAD_VERSION";

	public static String LEAD_TYPE = "LEAD_TYPE";

	public static String LEAD_ARCH = "LEAD_ARCH";

	public static String LEAD_NAME = "LEAD_NAME";

	public static String LEAD_OS = "LEAD_OS";

	public static String RPMTAG_NAME = "1000";

	public static String RPMTAG_VERSION = "1001";

	public static String RPMTAG_RELEASE = "1002";

	public static String RPMTAG_EPOCH = "1003";

	public static String RPMTAG_SUMMARY = "1004";

	public static String RPMTAG_DESCRIPTION = "1005";

	public static String RPMTAG_BUILDTIME = "1006";

	public static String RPMTAG_BUILDHOST = "1007";

	public static String RPMTAG_INSTALLTIME = "1008";

	public static String RPMTAG_SIZE = "1009";

	public static String RPMTAG_DISTRIBUTION = "1010";

	public static String RPMTAG_VENDOR = "1011";

	public static String RPMTAG_GIF = "1012";

	public static String RPMTAG_XPM = "1013";

	public static String RPMTAG_LICENSE = "1014";

	public static String RPMTAG_PACKAGER = "1015";

	public static String RPMTAG_GROUP = "1016";

	public static String RPMTAG_CHANGELOG = "1017";

	public static String RPMTAG_SOURCE = "1018";

	public static String RPMTAG_PATCH = "1019";

	public static String RPMTAG_URL = "1020";

	public static String RPMTAG_OS = "1021";

	public static String RPMTAG_ARCH = "1022";

	public static String RPMTAG_PREIN = "1023";

	public static String RPMTAG_POSTIN = "1024";

	public static String RPMTAG_PREUN = "1025";

	public static String RPMTAG_POSTUN = "1026";

	public static String RPMTAG_OLDFILENAMES = "1027";

	public static String RPMTAG_FILESIZES = "1028";

	public static String RPMTAG_FILESTATES = "1029";

	public static String RPMTAG_FILEMODES = "1030";

	public static String RPMTAG_FILEUIDS = "1031";

	public static String RPMTAG_FILEGIDS = "1032";

	public static String RPMTAG_FILERDEVS = "1033";

	public static String RPMTAG_FILEMTIMES = "1034";

	public static String RPMTAG_FILEMD5S = "1035";

	public static String RPMTAG_FILELINKTOS = "1036";

	public static String RPMTAG_FILEFLAGS = "1037";

	public static String RPMTAG_ROOT = "1038";

	public static String RPMTAG_FILEUSERNAME = "1039";

	public static String RPMTAG_FILEGROUPNAME = "1040";

	public static String RPMTAG_EXCLUDE = "1041";

	public static String RPMTAG_EXCLUSIVE = "1042";

	public static String RPMTAG_ICON = "1043";

	public static String RPMTAG_SOURCERPM = "1044";

	public static String RPMTAG_FILEVERIFYFLAGS = "1045";

	public static String RPMTAG_ARCHIVESIZE = "1046";

	public static String RPMTAG_PROVIDENAME = "1047";

	public static String RPMTAG_REQUIREFLAGS = "1048";

	public static String RPMTAG_REQUIRENAME = "1049";

	public static String RPMTAG_REQUIREVERSION = "1050";

	public static String RPMTAG_NOSOURCE = "1051";

	public static String RPMTAG_NOPATCH = "1052";

	public static String RPMTAG_CONFLICTFLAGS = "1053";

	public static String RPMTAG_CONFLICTNAME = "1054";

	public static String RPMTAG_CONFLICTVERSION = "1055";

	public static String RPMTAG_DEFAULTPREFIX = "1056";

	public static String RPMTAG_BUILDROOT = "1057";

	public static String RPMTAG_INSTALLPREFIX = "1058";

	public static String RPMTAG_EXCLUDEARCH = "1059";

	public static String RPMTAG_EXCLUDEOS = "1060";

	public static String RPMTAG_EXCLUSIVEARCH = "1061";

	public static String RPMTAG_EXCLUSIVEOS = "1062";

	public static String RPMTAG_AUTOREQPROV = "1063";

	public static String RPMTAG_RPMVERSION = "1064";

	public static String RPMTAG_TRIGGERSCRIPTS = "1065";

	public static String RPMTAG_TRIGGERNAME = "1066";

	public static String RPMTAG_TRIGGERVERSION = "1067";

	public static String RPMTAG_TRIGGERFLAGS = "1068";

	public static String RPMTAG_TRIGGERINDEX = "1069";

	public static String RPMTAG_VERIFYSCRIPT = "1079";

	public static String RPMTAG_CHANGELOGTIME = "1080";

	public static String RPMTAG_CHANGELOGNAME = "1081";

	public static String RPMTAG_CHANGELOGTEXT = "1082";

	public static String RPMTAG_BROKENMD5 = "1083";

	public static String RPMTAG_PREREQ = "1084";

	public static String RPMTAG_PREINPROG = "1085";

	public static String RPMTAG_POSTINPROG = "1086";

	public static String RPMTAG_PREUNPROG = "1087";

	public static String RPMTAG_POSTUNPROG = "1088";

	public static String RPMTAG_BUILDARCHS = "1089";

	public static String RPMTAG_OBSOLETENAME = "1090";

	public static String RPMTAG_VERIFYSCRIPTPROG = "1091";

	public static String RPMTAG_TRIGGERSCRIPTPROG = "1092";

	public static String RPMTAG_DOCDIR = "1093";

	public static String RPMTAG_COOKIE = "1094";

	public static String RPMTAG_FILEDEVICES = "1095";

	public static String RPMTAG_FILEINODES = "1096";

	public static String RPMTAG_FILELANGS = "1097";

	public static String RPMTAG_PREFIXES = "1098";

	public static String RPMTAG_INSTPREFIXES = "1099";

	public static String RPMTAG_TRIGGERIN = "1100";

	public static String RPMTAG_TRIGGERUN = "1101";

	public static String RPMTAG_TRIGGERPOSTUN = "1102";

	public static String RPMTAG_AUTOREQ = "1103";

	public static String RPMTAG_AUTOPROV = "1104";

	public static String RPMTAG_CAPABILITY = "1105";

	public static String RPMTAG_SOURCEPACKAGE = "1106";

	public static String RPMTAG_OLDORIGFILENAMES = "1107";

	public static String RPMTAG_BUILDPREREQ = "1108";

	public static String RPMTAG_BUILDREQUIRES = "1109";

	public static String RPMTAG_BUILDCONFLICTS = "1110";

	public static String RPMTAG_BUILDMACROS = "1111";

	public static String RPMTAG_PROVIDEFLAGS = "1112";

	public static String RPMTAG_PROVIDEVERSION = "1113";

	public static String RPMTAG_OBSOLETEFLAGS = "1114";

	public static String RPMTAG_OBSOLETEVERSION = "1115";

	public static String RPMTAG_DIRINDEXES = "1116";

	public static String RPMTAG_BASENAMES = "1117";

	public static String RPMTAG_DIRNAMES = "1118";

	public static String RPMTAG_ORIGDIRINDEXES = "1119";

	public static String RPMTAG_ORIGBASENAMES = "1120";

	public static String RPMTAG_ORIGDIRNAMES = "1121";

	public static String RPMTAG_OPTFLAGS = "1122";

	public static String RPMTAG_DISTURL = "1123";

	public static String RPMTAG_PAYLOADFORMAT = "1124";

	public static String RPMTAG_PAYLOADCOMPRESSOR = "1125";

	public static String RPMTAG_PAYLOADFLAGS = "1126";

	public static String RPMTAG_INSTALLCOLOR = "1127";

	public static String RPMTAG_INSTALLTID = "1128";

	public static String RPMTAG_REMOVETID = "1129";

	public static String RPMTAG_SHA1RHN = "1130";

	public static String RPMTAG_RHNPLATFORM = "1131";

	public static String RPMTAG_PLATFORM = "1132";

	public static String RPMTAG_PATCHESNAME = "1133";

	public static String RPMTAG_PATCHESFLAGS = "1134";

	public static String RPMTAG_PATCHESVERSION = "1135";

	public static String RPMTAG_CACHECTIME = "1136";

	public static String RPMTAG_CACHEPKGPATH = "1137";

	public static String RPMTAG_CACHEPKGSIZE = "1138";

	public static String RPMTAG_CACHEPKGMTIME = "1139";

	public static String RPMTAG_FILECOLORS = "1140";

	public static String RPMTAG_FILECLASS = "1141";

	public static String RPMTAG_CLASSDICT = "1142";

	public static String RPMTAG_FILEDEPENDSX = "1143";

	public static String RPMTAG_FILEDEPENDSN = "1144";

	public static String RPMTAG_DEPENDSDICT = "1145";

	public static String RPMTAG_SOURCEPKGID = "1146";

	public static String RPMTAG_FILECONTEXTS = "1147";

	public static String RPMTAG_FSCONTEXTS = "1148";

	public static String RPMTAG_RECONTEXTS = "1149";

	public static String RPMTAG_POLICIES = "1150";

	public static String RPMTAG_PRETRANS = "1151";

	public static String RPMTAG_POSTTRANS = "1152";

	public static String RPMTAG_PRETRANSPROG = "1153";

	public static String RPMTAG_POSTTRANSPROG = "1154";

	public static String RPMTAG_DISTTAG = "1155";

	public static String RPMTAG_SUGGESTSNAME = "1156";

	public static String RPMTAG_SUGGESTSVERSION = "1157";

	public static String RPMTAG_SUGGESTSFLAGS = "1158";

	public static String RPMTAG_ENHANCESNAME = "1159";

	public static String RPMTAG_ENHANCESVERSION = "1160";

	public static String RPMTAG_ENHANCESFLAGS = "1161";

	public static String RPMTAG_PRIORITY = "1162";
	
	public static int RPMSENSE_LESS = 0x02;
	
	public static int RPMSENSE_GREATER = 0x04;
	
	public static int RPMSENSE_EQUAL = 0x08;
	
	public static int RPMSENSE_PREREQ = 0x40;

	public static int RPMSENSE_INTERP = 0x100;
	
	public static int RPMSENSE_SCRIPT_PRE = 0x200; 
	
	public static int RPMSENSE_SCRIPT_POST = 0x400;	 
	
	public static int RPMSENSE_SCRIPT_PREUN = 0x800;	 
	
	public static int RPMSENSE_SCRIPT_POSTUN = 0x1000;	 
	
	public static int RPMSENSE_RPMLIB = 0x1000000;

	private Hashtable header = new Hashtable();

	private Hashtable signature = new Hashtable();

	private Hashtable lead = new Hashtable();

	public RPMFile(File file) throws MalformedURLException, IOException {
		loadData(file);
	}

	private void loadData(File f) throws IOException {
		RandomAccessFile file = new RandomAccessFile(f, "r");

		lead = readLead(file);
		signature = readSignature(file);
		header = readHeader(file);
	}

	private Hashtable readLead(RandomAccessFile file) throws IOException {
		Hashtable table = new Hashtable();

		byte[] magic = new byte[4];
		file.read(magic);

		if (!HexUtils.bytesToHex(magic).equalsIgnoreCase("edabeedb")) {
			// TODO: throw exception (not RPM file)
		}

		byte[] version = new byte[2];
		file.read(version);

		if (!HexUtils.bytesToHex(version).startsWith("03")) {
			// TODO: throw exception not a version 3 RPM
		} else {
			table.put(LEAD_VERSION, HexUtils.byteToHex(version[0]) + "."
					+ HexUtils.byteToHex(version[1]));
		}

		byte[] type = new byte[2];
		file.read(type);
		table.put(LEAD_TYPE, new Integer(HexUtils.bytesToInt(type)));

		byte[] arch = new byte[2];
		file.read(arch);
		if (HexUtils.bytesToHex(type).equalsIgnoreCase("0000")) {
			table.put(LEAD_ARCH, HexUtils.bytesToHex(arch));
		}

		byte[] name = new byte[66];
		file.read(name);
		table.put(LEAD_NAME, new String(name).trim());

		byte[] os = new byte[2];
		file.read(os);
		table.put(LEAD_OS, new Integer(HexUtils.bytesToInt(os)));

		return table;
	}

	private Hashtable readSignature(RandomAccessFile file) throws IOException {
		return readEntries(file, getSignaturePosition(file));
	}

	private Hashtable readHeader(RandomAccessFile file) throws IOException {
		return readEntries(file, getHeaderPosition(file));
	}

	private Hashtable readEntries(RandomAccessFile file, long sectionPosition)
			throws IOException {
		Hashtable table = new Hashtable();

		file.seek(sectionPosition);

		byte[] sigMagic = new byte[3];
		file.read(sigMagic);

		if (!HexUtils.bytesToHex(sigMagic).equalsIgnoreCase("8eade8")) {
			// TODO: throw exception (expected signature to be here)
		}

		file.skipBytes(1); // version

		file.skipBytes(4); // reserved bytes

		byte[] numEntries = new byte[4];
		file.read(numEntries);

		file.skipBytes(4); // size of store

		int entries = HexUtils.bytesToInt(numEntries);

		// Number of entries times the length of indexes plus the start of the
		// indexes.
		long storePos = entries * 16 + file.getFilePointer();

		RPMIndexEntry[] indexEntries = new RPMIndexEntry[entries];

		file.seek(sectionPosition + 16); // go to the start of the entries
											// just in case we're not already
											// there.

		for (int i = 0; i < entries; i++) {
			int tag = file.readInt();
			int type = file.readInt();
			long offset = file.readInt() + storePos;
			int count = file.readInt();

			indexEntries[i] = new RPMIndexEntry(tag, type, offset, count);
		}

		for (int i = 0; i < indexEntries.length; i++) {
			indexEntries[i].parseData(file);

			table.put(new Integer(indexEntries[i].getTag()).toString(),
					indexEntries[i]);
		}

		return table;
	}

	private long getHeaderPosition(RandomAccessFile file) throws IOException {
		file.seek(getSignaturePosition(file));

		byte[] magic = new byte[3];
		file.read(magic);

		if (!HexUtils.bytesToHex(magic).equalsIgnoreCase("8eade8")) {
			throw new IOException();
		}

		file.skipBytes(5); // version and reserved bytes

		byte[] numEntriesInBytes = new byte[4];
		file.read(numEntriesInBytes);
		int numEntries = HexUtils.bytesToInt(numEntriesInBytes);

		byte[] storeSizeInBytes = new byte[4];
		file.read(storeSizeInBytes);
		int storeSize = HexUtils.bytesToInt(storeSizeInBytes);

		file.seek(numEntries * 16 + 112 + storeSize);

		magic[0] = file.readByte();
		while (magic[0] != (byte) 0x8e) {
			magic[0] = file.readByte();
		}
		magic[1] = file.readByte();
		magic[2] = file.readByte();

		if (!HexUtils.bytesToHex(magic).equalsIgnoreCase("8eade8")) {
			throw new IOException();
		}

		return file.getFilePointer() - 3;
	}

	private long getSignaturePosition(RandomAccessFile file) {
		return 96;
	}

	public String getLeadEntry(String tag) {
		return lead.get(tag).toString();
	}

	public RPMIndexEntry getSignatureEntry(String tag) {
		return (RPMIndexEntry) signature.get(tag);
	}

	public RPMIndexEntry getHeaderEntry(String tag) {
		return (RPMIndexEntry) header.get(tag);
	}

	public Enumeration getLeadKeys() {
		return lead.keys();
	}

	public Enumeration getSignatureKeys() {
		return signature.keys();
	}

	public Enumeration getHeaderKeys() {
		return header.keys();
	}
}
