/**
 * Copyright 2012 Kamran Zafar 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 * 
 */

package org.kamranzafar.jtar;

import java.io.File;
import java.util.Date;

/**
 * @author Kamran Zafar
 * 
 */
public class TarEntry {
	protected File file;
	protected TarHeader header;

	private TarEntry() {
		this.file = null;
		header = new TarHeader();
	}

	public TarEntry(File file, String entryName) {
		this();
		this.file = file;
		this.extractTarHeader(entryName);
	}

	public TarEntry(byte[] headerBuf) {
		this();
		this.parseTarHeader(headerBuf);
	}

	/**
	 * Constructor to create an entry from an existing TarHeader object.
	 * 
	 * This method is useful to add new entries programmatically (e.g. for
	 * adding files or directories that do not exist in the file system).
	 */
	public TarEntry(TarHeader header) {
		this.file = null;
		this.header = header;
	}

	@Override
	public boolean equals(Object it) {
		if (!(it instanceof TarEntry)) {
			return false;
		}
		return header.name.toString().equals(
				((TarEntry) it).header.name.toString());
	}

	@Override
	public int hashCode() {
		return header.name.hashCode();
	}

	public boolean isDescendent(TarEntry desc) {
		return desc.header.name.toString().startsWith(header.name.toString());
	}

	public TarHeader getHeader() {
		return header;
	}

	public String getName() {
		String name = header.name.toString();
		if (header.namePrefix != null && !header.namePrefix.toString().equals("")) {
			name = header.namePrefix.toString() + "/" + name;
		}

		return name;
	}

	public void setName(String name) {
		header.name = new StringBuffer(name);
	}

	public int getUserId() {
		return header.userId;
	}

	public void setUserId(int userId) {
		header.userId = userId;
	}

	public int getGroupId() {
		return header.groupId;
	}

	public void setGroupId(int groupId) {
		header.groupId = groupId;
	}

	public String getUserName() {
		return header.userName.toString();
	}

	public void setUserName(String userName) {
		header.userName = new StringBuffer(userName);
	}

	public String getGroupName() {
		return header.groupName.toString();
	}

	public void setGroupName(String groupName) {
		header.groupName = new StringBuffer(groupName);
	}

	public void setIds(int userId, int groupId) {
		this.setUserId(userId);
		this.setGroupId(groupId);
	}

	public void setModTime(long time) {
		header.modTime = time / 1000;
	}

	public void setModTime(Date time) {
		header.modTime = time.getTime() / 1000;
	}

	public Date getModTime() {
		return new Date(header.modTime * 1000);
	}

	public File getFile() {
		return this.file;
	}

	public long getSize() {
		return header.size;
	}

	public void setSize(long size) {
		header.size = size;
	}

	/**
	 * Checks if the org.kamrazafar.jtar entry is a directory
	 */
	public boolean isDirectory() {
		if (this.file != null)
			return this.file.isDirectory();

		if (header != null) {
			if (header.linkFlag == TarHeader.LF_DIR)
				return true;

			if (header.name.toString().endsWith("/"))
				return true;
		}

		return false;
	}

	/**
	 * Extract header from File
	 */
	public void extractTarHeader(String entryName) {
		int permissions = PermissionUtils.permissions(file);
		header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory(), permissions);
	}

	/**
	 * Calculate checksum
	 */
	public long computeCheckSum(byte[] buf) {
		long sum = 0;

		for (int i = 0; i < buf.length; ++i) {
			sum += 255 & buf[i];
		}

		return sum;
	}

	/**
	 * Writes the header to the byte buffer
	 */
	public void writeEntryHeader(byte[] outbuf) {
		int offset = 0;

		offset = TarHeader.getNameBytes(header.name, outbuf, offset, TarHeader.NAMELEN);
		offset = Octal.getOctalBytes(header.mode, outbuf, offset, TarHeader.MODELEN);
		offset = Octal.getOctalBytes(header.userId, outbuf, offset, TarHeader.UIDLEN);
		offset = Octal.getOctalBytes(header.groupId, outbuf, offset, TarHeader.GIDLEN);

		long size = header.size;

		offset = Octal.getLongOctalBytes(size, outbuf, offset, TarHeader.SIZELEN);
		offset = Octal.getLongOctalBytes(header.modTime, outbuf, offset, TarHeader.MODTIMELEN);

		int csOffset = offset;
		for (int c = 0; c < TarHeader.CHKSUMLEN; ++c)
			outbuf[offset++] = (byte) ' ';

		outbuf[offset++] = header.linkFlag;

		offset = TarHeader.getNameBytes(header.linkName, outbuf, offset, TarHeader.NAMELEN);
		offset = TarHeader.getNameBytes(header.magic, outbuf, offset, TarHeader.USTAR_MAGICLEN);
		offset = TarHeader.getNameBytes(header.userName, outbuf, offset, TarHeader.USTAR_USER_NAMELEN);
		offset = TarHeader.getNameBytes(header.groupName, outbuf, offset, TarHeader.USTAR_GROUP_NAMELEN);
		offset = Octal.getOctalBytes(header.devMajor, outbuf, offset, TarHeader.USTAR_DEVLEN);
		offset = Octal.getOctalBytes(header.devMinor, outbuf, offset, TarHeader.USTAR_DEVLEN);
		offset = TarHeader.getNameBytes(header.namePrefix, outbuf, offset, TarHeader.USTAR_FILENAME_PREFIX);

		for (; offset < outbuf.length;)
			outbuf[offset++] = 0;

		long checkSum = this.computeCheckSum(outbuf);

		Octal.getCheckSumOctalBytes(checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN);
	}

	/**
	 * Parses the tar header to the byte buffer
	 */
	public void parseTarHeader(byte[] bh) {
		int offset = 0;

		header.name = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
		offset += TarHeader.NAMELEN;

		header.mode = (int) Octal.parseOctal(bh, offset, TarHeader.MODELEN);
		offset += TarHeader.MODELEN;

		header.userId = (int) Octal.parseOctal(bh, offset, TarHeader.UIDLEN);
		offset += TarHeader.UIDLEN;

		header.groupId = (int) Octal.parseOctal(bh, offset, TarHeader.GIDLEN);
		offset += TarHeader.GIDLEN;

		header.size = Octal.parseOctal(bh, offset, TarHeader.SIZELEN);
		offset += TarHeader.SIZELEN;

		header.modTime = Octal.parseOctal(bh, offset, TarHeader.MODTIMELEN);
		offset += TarHeader.MODTIMELEN;

		header.checkSum = (int) Octal.parseOctal(bh, offset, TarHeader.CHKSUMLEN);
		offset += TarHeader.CHKSUMLEN;

		header.linkFlag = bh[offset++];

		header.linkName = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
		offset += TarHeader.NAMELEN;

		header.magic = TarHeader.parseName(bh, offset, TarHeader.USTAR_MAGICLEN);
		offset += TarHeader.USTAR_MAGICLEN;

		header.userName = TarHeader.parseName(bh, offset, TarHeader.USTAR_USER_NAMELEN);
		offset += TarHeader.USTAR_USER_NAMELEN;

		header.groupName = TarHeader.parseName(bh, offset, TarHeader.USTAR_GROUP_NAMELEN);
		offset += TarHeader.USTAR_GROUP_NAMELEN;

		header.devMajor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN);
		offset += TarHeader.USTAR_DEVLEN;

		header.devMinor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN);
		offset += TarHeader.USTAR_DEVLEN;

		header.namePrefix = TarHeader.parseName(bh, offset, TarHeader.USTAR_FILENAME_PREFIX);
	}
}