/**
*
* Copyright 2013 Radek Henys
* 
* This file is part of Spelling Alphabet Trainer.
*
* Spelling Alphabet Trainer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Spelling Alphabet Trainer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Spelling Alphabet Trainer.  If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.mouseviator.utils.internatialization;

import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>This class stores information about language file. Ok, just the path to it, but contains methods to obtain
 * different parts of this path. Also, the file has to be named to match the {@link #FILENAME_PATTERN} regular expression -
 * it must contain language a country code to construct the locale. Valid name is for example: My_languageFile-Name_en_US.properties
 * 
 * @author Murdock
 */
public class LanguageFile {
    /*
     * Path to the language file.
     */
    private String filePath;
    /**
     * Regular expression the file name must match.
     */
    public static final String FILENAME_PATTERN = "^(.*?)(_)([\\w]{2})(_)([\\w]{2})(.properties)$";
    private Pattern pattern = Pattern.compile(FILENAME_PATTERN, Pattern.CASE_INSENSITIVE);

    /**
     * Default constructor. Takes filePath as argument. Performs validation of the filePath.
     * 
     * @param filePath Path to the language file.
     * @throws IOException If the filePath is not a file or does not exists.
     * @throws InvalidParameterException If the file path (name) does not match {@link #FILENAME_PATTERN} the regular expression.
     * @throws NullPointerException If filePath is null.
     */
    public LanguageFile(String filePath) throws IOException, InvalidParameterException, NullPointerException {
        checkFilePath(filePath);
        this.filePath = filePath;
    }

    /**
     * Returns path to the language file.
     * 
     * @return The path to the language file.
     */
    public String getFilePath() {
        return filePath;
    }

    /**
     * This method sets the path to language file. It performs validation of the path.
     * 
     * @param fileName the fileName to set
     * @throws IOException If the filePath is not a file or does not exists.
     * @throws InvalidParameterException If the file path (name) does not match {@link #FILENAME_PATTERN} the regular expression.
     * @throws NullPointerException If filePath is null.
     */
    public void setFilePath(String fileName) throws IOException, InvalidParameterException, NullPointerException {
        checkFilePath(filePath);
        this.filePath = fileName;
    }

    /**
     * Returns folder path of the path to the language file. So if the language file path is:
     * /lang/English_en_US.properties, this function will return: /lang/
     * 
     * @return Folder path part of the path to the language file.
     */
    public String getFolderPath() {
        return getFolderPath(filePath);
    }

    /**
     * Function returns folder path part of given file path. So if the language file path is:
     * /lang/English_en_US.properties, this function will return: /lang/
     * 
     * @param filePath File path we want the folder path from.
     * @return Folder path part of given file path.
     */
    private String getFolderPath(String filePath) {
        int lastPos = -1;
        int charPos = filePath.lastIndexOf("\\");
        lastPos = Math.max(lastPos, charPos);
        charPos = filePath.lastIndexOf("/");
        lastPos = Math.max(lastPos, charPos);
        if (lastPos != -1) {
            return filePath.substring(0, lastPos);
        } else {
            return filePath;
        }
    }

    /**
     * This function returns file name part of the path to the language file. So if the language file path is:
     * /lang/English_en_US.properties, this function will return: English_en_US.properties.
     * 
     * @return File name part of the path to the language file.
     */
    public String getFileName() {
        return getFileName(filePath);
    }

    /**
     * This function will return file name part from given file path. So if the language file path is:
     * /lang/English_en_US.properties, this function will return: English_en_US.properties.
     * 
     * @param filePath File path we want the file name part from.
     * @return File name part from given file path.
     */
    private String getFileName(String filePath) {
        int lastPos = -1;
        int charPos = filePath.lastIndexOf("\\");
        lastPos = Math.max(lastPos, charPos);
        charPos = filePath.lastIndexOf("/");
        lastPos = Math.max(lastPos, charPos);
        if (lastPos != -1) {
            return filePath.substring(lastPos + 1);
        } else {
            return filePath;
        }
    }

    /**
     * Returns the name of the language file with locale and without extension. If the language file path is:
     * /lang/English_en_US.properties, this function will return: English_en_US
     * 
     * @return Name of the language file with locale and without extension.
     */
    public String getNameWithLocale() {
        return getNameWithLocale(filePath);
    }

    /**
     * Returns the name of the language file with locale and without extension. If the language file path is:
     * /lang/English_en_US.properties, this function will return: English_en_US
     * 
     * @param filePath File path we want the name with locale part from.
     * @return Name of the file with locale and without extension.
     */
    private String getNameWithLocale(String filePath) {
        //we first need file name
        String fileName = getFileName(filePath);
        //than we can match only thye parts we need
        Matcher matcher = pattern.matcher(fileName);
        if (matcher.matches()) {
            return matcher.group(1) + matcher.group(2) + matcher.group(3) + matcher.group(4) + matcher.group(5);
        } else {
            throw new InvalidParameterException(MessageFormat.format(ResourceBundle.getBundle(Constants.TRANSLATION_RESOURCE_BUNDLE).getString("LanguageFile.getNameWithLocale.regex_exception"), filePath, FILENAME_PATTERN));
        }
    }

    /**
     * Returns the name of the language file without locale part and without extension. If the language file path is:
     * /lang/English_en_US.properties, this function will return: English
     * 
     * @return Name of the language file without locale part and without extension.
     */
    public String getName() {
        return getName(filePath);
    }

    /**
     * Returns the name of the language file without locale part and without extension. If the language file path is:
     * /lang/English_en_US.properties, this function will return: English
     * 
     * @param filePath File path we wat the name part from.
     * @return Name of the file without locale part and without extension.
     */
    private String getName(String filePath) {
        //we first need file name
        String fileName = getFileName(filePath);
        //than we can match only thye parts we need
        Matcher matcher = pattern.matcher(fileName);
        if (matcher.matches()) {
            return matcher.group(1);
        } else {
            throw new InvalidParameterException(MessageFormat.format(ResourceBundle.getBundle(Constants.TRANSLATION_RESOURCE_BUNDLE).getString("LanguageFile.getName.regex_exception"), filePath, FILENAME_PATTERN));
        }
    }

    /**
     * Returns the {@see Locale} of this language file.
     * 
     * @return Locale of language file.
     */
    public Locale getLocale() {
        Matcher matcher = pattern.matcher(getFileName(filePath));
        //check matches
        if (matcher.matches()) {
            return new Locale(matcher.group(3), matcher.group(5));
        } else {
            return null;
        }
    }

    /**
     * This method checks if the given filePath is path to valid file. That means, if the file exist and if it is a file (not directory).
     * It also checks if the given filePath matches {@link #FILENAME_PATTERN}. If any of these rules is broken, an exception is thrown.
     * Otherwise, the filePath is valid.
     * 
     * @param filePath The language file path we want to check.
     * @throws IOException If filePath does not exist or is not a file.
     * @throws InvalidParameterException If filePath does not match {@link #FILENAME_PATTERN}.
     * @throws NullPointerException If filePath is null.
     */
    private void checkFilePath(String filePath) throws IOException, InvalidParameterException, NullPointerException {
        //First check the file name against the regex
        if (filePath == null) {
            throw new NullPointerException(ResourceBundle.getBundle(Constants.TRANSLATION_RESOURCE_BUNDLE).getString("LanguageFile.checkFilePath.null_exception"));
        }
        Matcher matcher = pattern.matcher(getFileName(filePath));
        if (!matcher.matches()) {
            throw new InvalidParameterException(MessageFormat.format(ResourceBundle.getBundle(Constants.TRANSLATION_RESOURCE_BUNDLE).getString("LanguageFile.checkFilePath.regex_exception"), filePath, FILENAME_PATTERN));
        }
        //than check the file
        File f = new File(filePath);
        if (!f.exists() || !f.isFile()) {
            throw new IOException(MessageFormat.format(ResourceBundle.getBundle(Constants.TRANSLATION_RESOURCE_BUNDLE).getString("LanguageFile.checkFilePath.io_exception"), filePath));
        }
    }
}
