/*
 * ftp4j - A pure Java FTP client library
 * 
 * Copyright (C) 2008-2010 Carlo Pelliccia (www.sauronsoftware.it)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version
 * 2.1, 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 Lesser General Public License 2.1 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License version 2.1 along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package it.sauronsoftware.ftp4j.listparsers;

import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPListParseException;
import it.sauronsoftware.ftp4j.FTPListParser;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * This parser can handle the standard MLST/MLSD responses (RFC 3659).
 * 
 * @author Carlo Pelliccia
 * @since 1.5
 */
public class MLSDListParser implements FTPListParser {

	/**
	 * Date format 1 for MLSD date facts (supports millis).
	 */
	private static final DateFormat MLSD_DATE_FORMAT_1 = new SimpleDateFormat("yyyyMMddHHmmss.SSS Z");

	/**
	 * Date format 2 for MLSD date facts (doesn't support millis).
	 */
	private static final DateFormat MLSD_DATE_FORMAT_2 = new SimpleDateFormat("yyyyMMddHHmmss Z");

	public FTPFile[] parse(String[] lines) throws FTPListParseException {
		ArrayList list = new ArrayList();
		for (int i = 0; i < lines.length; i++) {
			FTPFile file = parseLine(lines[i]);
			if (file != null) {
				list.add(file);
			}
		}
		int size = list.size();
		FTPFile[] ret = new FTPFile[size];
		for (int i = 0; i < size; i++) {
			ret[i] = (FTPFile) list.get(i);
		}
		return ret;
	}

	/**
	 * Parses a line ad a MLSD response element.
	 * 
	 * @param line
	 *            The line.
	 * @return The file, or null if the line has to be ignored.
	 * @throws FTPListParseException
	 *             If the line is not a valid MLSD entry.
	 */
	private FTPFile parseLine(String line) throws FTPListParseException {
		// According to Extensions to FTP RFC 3659, the file name in a MLSD list response line come after the first space in the line  : https://tools.ietf.org/html/rfc3659
		// List line format is <FACTS WITH NO SPACES SEPARATED WITH SEMICOLON> <SPACE> <FILENAME>
		// Example of line that failed before: Type=file;Size=25730;Modify=19940728095854;Perm=; cap;mux.tar.z
		int nameIndex = line.indexOf(" ");

		// Throw exception if no name in response line
		if (nameIndex == -1) {
			throw new FTPListParseException();
		}

		// Extract the file name.
		String name = line.substring(nameIndex + 1);

		// Extract the facts string
		String factsLine = line.substring(0, nameIndex);
		ArrayList list = new ArrayList();
		StringTokenizer st = new StringTokenizer(factsLine, ";");
		while (st.hasMoreElements()) {
			String aux = st.nextToken().trim();
			if (aux.length() > 0) {
				list.add(aux);
			}
		}

		// If no facts, throw exception
		if (list.size() == 0) {
			throw new FTPListParseException();
		}

		// Parses the facts.
		Properties facts = new Properties();
		for (Iterator i = list.iterator(); i.hasNext();) {
			String aux = (String) i.next();
			int sep = aux.indexOf('=');
			if (sep == -1) {
				throw new FTPListParseException();
			}
			String key = aux.substring(0, sep).trim();
			String value = aux.substring(sep + 1, aux.length()).trim();
			if (key.length() == 0 || value.length() == 0) {
				throw new FTPListParseException();
			}
			facts.setProperty(key, value);
		}
		// Type.
		int type;
		String typeString = facts.getProperty("type");
		if (typeString == null) {
			throw new FTPListParseException();
		} else if ("file".equalsIgnoreCase(typeString)) {
			type = FTPFile.TYPE_FILE;
		} else if ("dir".equalsIgnoreCase(typeString)) {
			type = FTPFile.TYPE_DIRECTORY;
		} else if ("cdir".equalsIgnoreCase(typeString)) {
			// Current directory. Skips...
			return null;
		} else if ("pdir".equalsIgnoreCase(typeString)) {
			// Parent directory. Skips...
			return null;
		} else {
			// Unknown... (link?)... Skips...
			return null;
		}
		// Last modification date.
		Date modifiedDate = null;
		String modifyString = facts.getProperty("modify");
		if (modifyString != null) {
			modifyString += " +0000";
			try {
				synchronized (MLSD_DATE_FORMAT_1) {
					modifiedDate = MLSD_DATE_FORMAT_1.parse(modifyString);
				}
			} catch (ParseException e1) {
				try {
					synchronized (MLSD_DATE_FORMAT_2) {
						modifiedDate = MLSD_DATE_FORMAT_2.parse(modifyString);
					}
				} catch (ParseException e2) {
					;
				}
			}
		}
		// Size.
		long size = 0;
		String sizeString = facts.getProperty("size");
		if (sizeString != null) {
			try {
				size = Long.parseLong(sizeString);
			} catch (NumberFormatException e) {
				;
			}
			if (size < 0) {
				size = 0;
			}
		}
		// Done!
		FTPFile ret = new FTPFile();
		ret.setType(type);
		ret.setModifiedDate(modifiedDate);
		ret.setSize(size);
		ret.setName(name);
		return ret;
	}

}