/*******************************************************************************
 * Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0-standalone.html
 ******************************************************************************/
package se.unlogic.standardutils.string;

import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.streams.StreamUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.UUID;

public class StringUtils {

   private static Properties LATIN1_ESCAPE_CHARACTERS = null;

   static {
      LATIN1_ESCAPE_CHARACTERS = new Properties();

      InputStream inputStream = StringUtils.class.getResourceAsStream("latin-1-escaped-character-set.properties");

      try {
         LATIN1_ESCAPE_CHARACTERS.load(inputStream);
      } catch (InvalidPropertiesFormatException e) {
         throw new RuntimeException(e);
      } catch (IOException e) {
         throw new RuntimeException(e);
      } finally {
         StreamUtils.closeStream(inputStream);
      }
   }

   /**
    * Unescapes escaped HTML characters
    *
    * Based on a fixed set of escaped HTML characters from the latin-1 set
    *
    * @param sequence
    *            - the secuence of characters to unescape characters in
    * @return - a sequenced without escaped HTML characters
    */
   public static String unEscapeHTML(String sequence) {

      return replaceCharacters(sequence, LATIN1_ESCAPE_CHARACTERS);
   }

   /**
    * Replaces characters in a character sequence
    *
    * @param sequence
    *            - the secuence of characters to replace characters in
    * @param characterSet
    *            - a set of key=value character mappings, e.g. &num;=#. Occurences of key, e.g. "&num;" will be replaced by value, e.g. "#"
    *
    * @return - a sequenced with replaced characters
    */
   public static String replaceCharacters(String sequence, Properties characterSet) {

      if (characterSet == null) {
         characterSet = LATIN1_ESCAPE_CHARACTERS;
      }

      for (Entry<Object, Object> entry : characterSet.entrySet()) {
         sequence = sequence.replaceAll(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
      }

      return sequence;
   }

   public static String removeHTMLTags(String sequence) {

      return sequence.replaceAll("\\<.*?>", "");
   }

   public static String parseUTF8(String encodedString)  {

      return new String(encodedString.getBytes(), StandardCharsets.UTF_8);
   }

   //TODO move
   public static boolean isValidUUID(String uuidstring) {

      try {
         UUID.fromString(uuidstring);
         return true;
      } catch (IllegalArgumentException e) {
         return false;
      }
   }

   //TODO move
   public static boolean isValidURL(String urlstring) {

      try {
         new URL(urlstring);
         return true;
      } catch (MalformedURLException e) {
         return false;
      }
   }

   public static String toQuotedCommaSeparatedString(List<? extends Object> list) {

      String arrayString = Arrays.deepToString(list.toArray());
      arrayString = arrayString.substring(1, arrayString.length() - 1);
      arrayString = "\"" + arrayString.replaceAll(", ", "\", \"") + "\"";
      return arrayString;
   }

   public static String toQuotedCommaSeparatedString(Object[] array) {

      String arrayString = Arrays.deepToString(array);
      arrayString = arrayString.substring(1, arrayString.length() - 1);
      arrayString = "\"" + arrayString.replaceAll(", ", "\", \"") + "\"";
      return arrayString;
   }

   public static String toCommaSeparatedString(Collection<? extends Object> list) {

      String arrayString = Arrays.deepToString(list.toArray());
      return arrayString.substring(1, arrayString.length() - 1);
   }

   public static String toCommaSeparatedString(Object[] array) {

      String arrayString = Arrays.deepToString(array);
      return arrayString.substring(1, arrayString.length() - 1);
   }

   public static String toCommaSeparatedString(List<? extends Object> list, Field field) throws IllegalArgumentException, IllegalAccessException {

      ReflectionUtils.fixFieldAccess(field);

      StringBuilder arrayString = new StringBuilder();

      for (int i = 0; i < list.size(); i++) {

         Object value = field.get(list.get(i));

         arrayString.append(value + ",");

      }

      return arrayString.substring(0, arrayString.length() - 1);

   }

   public static String readFileAsString(String filePath) throws java.io.IOException {

      StringBuffer fileData = new StringBuffer(1000);
      BufferedReader reader = new BufferedReader(new FileReader(filePath));
      char[] buf = new char[1024];
      int numRead = 0;
      while ((numRead = reader.read(buf)) != -1) {
         fileData.append(buf, 0, numRead);
      }
      reader.close();
      return fileData.toString();
   }

   public static String readFileAsString(File file) throws java.io.IOException {

      StringBuffer fileData = new StringBuffer(1000);
      BufferedReader reader = new BufferedReader(new FileReader(file));
      char[] buf = new char[1024];
      int numRead = 0;
      while ((numRead = reader.read(buf)) != -1) {
         fileData.append(buf, 0, numRead);
      }
      reader.close();
      return fileData.toString();
   }

   public static String readStreamAsString(InputStream inputStream) throws java.io.IOException {

      StringBuilder fileData = new StringBuilder(1000);
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
      char[] buf = new char[1024];
      int numRead = 0;
      while ((numRead = reader.read(buf)) != -1) {
         fileData.append(buf, 0, numRead);
      }
      reader.close();
      return fileData.toString();
   }

   public static boolean isEmpty(String string) {

      if (string == null) {
         return true;
      } else if (string.trim().length() == 0) {
         return true;
      } else {
         return false;
      }
   }

   public static String substring(String string, int maxChars, String suffix) {

      if (string.length() > maxChars) {
         return string.substring(0, maxChars - 1) + suffix;
      }

      return string;
   }

   public static String toLogFormat(String string, int maxLength) {

      if (string != null) {

         String returnString = substring(string, maxLength, "...");

         return returnString.replace("\n", " ").replace("\r", " ").replace("\t", " ");
      }

      return string;
   }

   public static String toSentenceCase(String string) {

      return string.substring(0, 1).toUpperCase() + string.toLowerCase().substring(1);
   }

   public static String toFirstLetterUppercase(String string) {

      return string.substring(0, 1).toUpperCase() + string.substring(1);
   }

   public static String toFirstLetterLowercase(String string) {

      return string.substring(0, 1).toLowerCase() + string.substring(1);
   }

   public static String repeatString(String string, int repetitionCount) {

      StringBuilder stringBuilder = new StringBuilder();

      repeatString(string, repetitionCount, stringBuilder);

      return stringBuilder.toString();
   }

   public static void repeatString(String string, int repetitionCount, StringBuilder stringBuilder) {

      if (repetitionCount >= 1) {

         for (int i = 1; i <= repetitionCount; i++) {

            stringBuilder.append(string);
         }
      }
   }

   /**
    *  Takes a string and splits it on linebreaks (\n) and also removes any linebreak (\n) and carriage return (\r) characters from the string segments
    *
    * @param string the string to be split
    * @return String array containing the split segments of the string
    */
   public static ArrayList<String> splitOnLineBreak(String string, boolean removeDuplicates){

      if(!StringUtils.isEmpty(string)){

         ArrayList<String> stringList = new ArrayList<String>();

         String[] lines = string.split("\n");

         for(String line : lines){

            line = line.replace("\n", "");
            line = line.replace("\r", "");

            if(!StringUtils.isEmpty(line) && (!removeDuplicates || !stringList.contains(line))){

               stringList.add(line);
            }
         }

         if(!stringList.isEmpty()){

            return stringList;
         }
      }

      return null;
   }

   public static int countOccurrences(String string, String snippet) {

      int count = 0;
      int index = 0;

      while ((index = string.indexOf(snippet, index)) != -1) {
         ++index;
         ++count;
      }

      return count;
   }
}
