/**
 *
 *  edtFTPj
 * 
 *  Copyright (C) 2000-2004 Enterprise Distributed Technologies Ltd
 *
 *  www.enterprisedt.com
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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 for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Bug fixes, suggestions and comments should be should posted on 
 *  http://www.enterprisedt.com/forums/index.php
 *
 *  Change Log:
 *
 *
 */
package com.enterprisedt.net.ftp;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import com.enterprisedt.util.debug.Logger;

/**
 *  Represents a remote OpenVMS file parser. Thanks to Jason Schultz for contributing
 *  significantly to this class 
 *
 *  @author      Bruce Blackshaw
 *  @version     $Revision: 1.8 $
 */
public class VMSFileParser extends FTPFileParser {

    /**
     *  Revision control id
     */
    public static String cvsId = "@(#)$Id: VMSFileParser.java,v 1.8 2009-06-22 09:08:53 bruceb Exp $";
             
    /**
     * Logging object
     */
    private static Logger log = Logger.getLogger("VMSFileParser");

    /**
     * Directory field
     */
    private final static String DIR = ".DIR";
    
    /**
     * Directory line
     */
    private final static String HDR = "Directory";
    
    /**
     * Total line
     */
    private final static String TOTAL = "Total";
    
    /**
     * Blocksize for calculating file sizes
     */
    private final static int DEFAULT_BLOCKSIZE = 512*1024;
    
    /**
     * Number of expected fields
     */
    private final static int MIN_EXPECTED_FIELD_COUNT = 4;
    
    /**
     * Date formatter
     */
    private SimpleDateFormat formatter1;
    
    /**
     * Date formatter
     */
    private SimpleDateFormat formatter2;
    
    /**
     * Is the version returned with the name?
     */
    private boolean versionInName = false;

    /**
     * Block size used to calculate size
     */
    private int blocksize = DEFAULT_BLOCKSIZE;
    
    /**
     * Constructor
     */
    public VMSFileParser() {
         setLocale(Locale.getDefault());
    }
    
    /**
     * Does this parser parse multiple lines to get one listing?
     * 
     * @return  
     */
    public boolean isMultiLine() {
        return true;
    }
    
    /**
     * Get the VMS blocksize, used for calculating file 
     * sizes
     * 
     * @return blocksize
     */
    public int getBlocksize() {
        return blocksize;
    }

    /**
     * Set the VMS blocksize, used for calculating file 
     * sizes. This might need to be changed if unexpected file
     * sizes are being returned for VMS files.
     * 
     * @param blocksize   new blocksize
     */
    public void setBlocksize(int blocksize) {
        this.blocksize = blocksize;
    }


    /**
     * Get the property that controls whether or not the version
     * number is returned as part of the filename, e.g. FILENAME.TXT;2
     * would be returned as is if this property is true, or FILENAME.TXT
     * if it is false.
     * 
     * @return true if version to be returned as part of the filename
     */
    public boolean isVersionInName() {
        return versionInName;
    }


    /**
     * Set the property that controls whether or not the version
     * number is returned as part of the filename, e.g. FILENAME.TXT;2
     * would be returned as is if this property is true, or FILENAME.TXT
     * if it is false.
     * 
     * @param versionInName     true if version to be returned as part of the filename
     */
    public void setVersionInName(boolean versionInName) {
        this.versionInName = versionInName;
    }
    
    public String toString() {
        return "VMS";
    }
    
    
    /**
     * Valid format for this parser
     * 
     * @param listing
     * @return true if valid
     */
    public boolean isValidFormat(String[] listing) {
        int count = Math.min(listing.length, 10);
        
        boolean semiColonName = false;
        boolean comma = false;
        boolean squareBracketStart = false, squareBracketEnd = false;

        for (int i = 0; i < count; i++) {
            if (listing[i].trim().length() == 0)
                continue;
            int pos = 0;
            if ((pos = listing[i].indexOf(';')) > 0 && (++pos < listing[i].length()) && 
                Character.isDigit(listing[i].charAt(pos)))
                semiColonName = true;
            if (listing[i].indexOf(',') > 0)
                comma = true;
            if (listing[i].indexOf('[') > 0)
                squareBracketStart = true;
            if (listing[i].indexOf(']') > 0)
                squareBracketEnd = true;
        }
        if (semiColonName && comma && squareBracketStart && squareBracketEnd)
            return true;
        log.debug("Not in VMS format");
        return false;
    }

    /**
     * Parse server supplied string
     *
     * OUTPUT: <begin>
     * 
     * Directory <dir>
     *  
     * <filename>
     *      used/allocated  dd-MMM-yyyy HH:mm:ss [unknown]      (PERMS)
     * <filename>
     *      used/allocated  dd-MMM-yyyy HH:mm:ss [unknown]      (PERMS)
     * ...
     * 
     * Total of <> files, <>/<> blocks
     *
     * @param raw   raw string to parse
     */
    public FTPFile parse(String raw) throws ParseException {
        String[] fields = split(raw);
        
        // skip blank lines
        if(fields.length <= 0)
        	return null;
        // skip line which lists Directory
        if (fields.length >= 2 && fields[0].compareTo(HDR) == 0)
        	return null;
        // skip line which lists Total
        if (fields.length > 0 && fields[0].compareTo(TOTAL) == 0)
        	return null;
        // probably the remainder of a listing on 2nd line
        if (fields.length < MIN_EXPECTED_FIELD_COUNT) 
            return null; 
        
        // first field is name
        String name = fields[0];
        
        // make sure it is the name (ends with ';<INT>')
        int semiPos = name.lastIndexOf(';');
        // check for ;
        if(semiPos <= 0) {
            log.warn("File version number not found in name '" + name + "'");
            return null;
        }
        
        String nameNoVersion = name.substring(0, semiPos);
        
        // check for version after ;
        String afterSemi = name.substring(semiPos+1);
        
        try{
            Integer.parseInt(afterSemi);
            // didn't throw exception yet, must be number
            // we don't use it currently but we might in future
        }
        catch(NumberFormatException ex) {
            // don't worry about version number
        }        
        
        // test is dir
        boolean isDir = false;
        if (nameNoVersion.endsWith(DIR)) 
        {
            isDir = true;
            name = nameNoVersion.substring(0, nameNoVersion.length()-DIR.length());
        }
        
        if (!versionInName && !isDir) {
            name = nameNoVersion;
        }
        
        // 2nd field is size USED/ALLOCATED format
        int slashPos = fields[1].indexOf('/');
        String sizeUsed = fields[1];
        if (slashPos > 0)
            sizeUsed = fields[1].substring(0, slashPos);
        long size = Long.parseLong(sizeUsed) * blocksize;
        
        // 3 & 4 fields are date time
        Date lastModified = null;
        try {
            lastModified = formatter1.parse(fields[2] + " " + fields[3]);
        }
        catch (ParseException ex) {
            try {
                lastModified = formatter2.parse(fields[2] + " " + fields[3]);
            }
            catch (ParseException ex1) {
                if (!ignoreDateParseErrors)
                    throw new DateParseException(ex.getMessage());
            }
        }
        
        // 5th field is [group,owner]
        String group = null;
        String owner = null;
        if (fields.length >= 5) {     
            if (fields[4].charAt(0) == '[' && fields[4].charAt(fields[4].length()-1) == ']') {
                int commaPos = fields[4].indexOf(',');
                if (commaPos < 0) {
                    group = owner = fields[4]; // just make them the same, e.g. SYSTEM
                }
                else {
	                group = fields[4].substring(1, commaPos);
	                owner = fields[4].substring(commaPos+1, fields[4].length()-1);
                }
            }
        }
        
        // 6th field is permissions e.g. (RWED,RWED,RE,)
        String permissions = null;
        if (fields.length >= 6) {     
            if (fields[5].charAt(0) == '(' && fields[5].charAt(fields[5].length()-1) == ')') {
                permissions = fields[5].substring(1, fields[5].length()-2);
            }
        }
        
        FTPFile file = new FTPFile(raw, name, size, isDir, lastModified); 
        file.setGroup(group);
        file.setOwner(owner);
        file.setPermissions(permissions);
        return file;        
    }

    /* (non-Javadoc)
     * @see com.enterprisedt.net.ftp.FTPFileParser#setLocale(java.util.Locale)
     */
    public void setLocale(Locale locale) {
        formatter1 = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss", locale);
        formatter2 = new SimpleDateFormat("dd-MMM-yyyy HH:mm", locale);
    }
  
}
