/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.ctecinf;

import java.awt.FontMetrics;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.Normalizer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Locale;
import javax.swing.JFormattedTextField;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 *
 * @author Cássio Conceição
 * @since 27/05/2019
 * @version 201905
 * @see http://ctecinf.com.br/
 */
public class Utils {

    private static final JFormattedTextField TEXT_FIELD = new JFormattedTextField();

    /**
     * Converte 'string' para hexadecimal
     *
     * @param value
     * @return String
     */
    public static String hexa(String value) {

        String digVal = "";

        for (int i = 0; i < value.length(); i++) {
            digVal = digVal + Integer.toHexString(value.charAt(i));
        }

        return digVal;
    }

    /**
     * Encriptografa SHA-1
     *
     * @param value
     * @return String
     * @throws NoSuchAlgorithmException
     */
    public static String sha1(String value) throws NoSuchAlgorithmException {
        byte[] messageDigest = MessageDigest.getInstance("SHA-1").digest(value.getBytes());
        return new BigInteger(1, messageDigest).toString(16);
    }

    /**
     * Encriptografa MD5
     *
     * @param value
     * @return String
     * @throws NoSuchAlgorithmException
     */
    public static String md5(String value) throws NoSuchAlgorithmException {
        byte[] messageDigest = MessageDigest.getInstance("MD5").digest(value.getBytes());
        return new BigInteger(1, messageDigest).toString(16);
    }

    /**
     * Calcula módulo 11
     *
     * @param chave
     * @return
     */
    public static int modulo11(String chave) {

        int total = 0;
        int peso = 2;

        for (int i = 0; i < chave.length(); i++) {

            total += (chave.charAt((chave.length() - 1) - i) - '0') * peso;
            peso++;

            if (peso == 10) {
                peso = 2;
            }
        }

        int resto = total % 11;

        return (resto == 0 || resto == 1) ? 0 : (11 - resto);
    }

    /**
     * Verifica se 'string' é um CPF válido
     *
     * @param cpf
     *
     * @return boolean
     */
    public static boolean isCPF(String cpf) {

        if (cpf == null) {
            return false;
        }

        cpf = cpf.replace(".", "").replace(" ", "").replace("-", "");

        if (cpf.equals("00000000000") || cpf.equals("11111111111")
                || cpf.equals("22222222222") || cpf.equals("33333333333")
                || cpf.equals("44444444444") || cpf.equals("55555555555")
                || cpf.equals("66666666666") || cpf.equals("77777777777")
                || cpf.equals("88888888888") || cpf.equals("99999999999")
                || cpf.length() != 11) {

            return false;
        }

        char dig10, dig11;
        int sm, i, r, num, peso;

        try {

            sm = 0;
            peso = 10;

            for (i = 0; i < 9; i++) {
                num = (int) (cpf.charAt(i) - 48);
                sm = sm + (num * peso);
                peso = peso - 1;
            }

            r = 11 - (sm % 11);

            if ((r == 10) || (r == 11)) {
                dig10 = '0';
            } else {
                dig10 = (char) (r + 48);
            }

            sm = 0;
            peso = 11;

            for (i = 0; i < 10; i++) {
                num = (int) (cpf.charAt(i) - 48);
                sm = sm + (num * peso);
                peso = peso - 1;
            }

            r = 11 - (sm % 11);

            if ((r == 10) || (r == 11)) {
                dig11 = '0';
            } else {
                dig11 = (char) (r + 48);
            }

            return (dig10 == cpf.charAt(9)) && (dig11 == cpf.charAt(10));

        } catch (InputMismatchException ex) {
            return false;
        }
    }

    /**
     * Converte data para padrão SEFAZ
     *
     * @return String
     */
    public static String date2NFe() {

        Calendar calendar = dateFromServer();

        String format = "yyyy-MM-dd'T'HH:mm:ss";

        if (calendar.get(Calendar.DST_OFFSET) == 0) {
            format += "'-03:00'";
        } else {
            format += "'-02:00'";
        }

        SimpleDateFormat df = new SimpleDateFormat(format);

        return df.format(calendar.getTime());
    }

    /**
     * Converte 'string' de data da NFe para objeto 'Date' Java
     *
     * @param date
     *
     * @return java.util.Date
     */
    public static Calendar dateNFe2Date(String date) {

        if (date == null || date.isEmpty()) {
            return null;
        }

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

        Date dt = null;

        try {
            dt = df.parse(date.replace("-03:00", "").replace("-02:00", ""));
        } catch (ParseException ex) {
        }

        Calendar cal = Calendar.getInstance(new Locale("pt", "BR"));
        cal.setTime(dt);

        return cal;
    }

    /**
     * Data e hora do servidor de internet
     *
     * @return Calendar
     */
    public static Calendar dateFromServer() {

        Calendar c = Calendar.getInstance(new Locale("pt", "BR"));

        try {

            String server = "http://ctecinf.com.br/current_time_millis.php";

            URL url = new URL(server);

            URLConnection conn = url.openConnection();
            conn.setConnectTimeout(4000);

            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

            String str = reader.readLine();

            c.setTimeInMillis(Long.parseLong(str));

        } catch (IOException | NumberFormatException ex) {
        }

        return c;
    }

    /**
     * Formata valor percentual
     *
     * @param value
     * @return
     */
    public static String perc2String(double value) {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getPercentInstance(new Locale("pt", "BR"));
        df.setMaximumFractionDigits(2);
        return df.format(value / 100);
    }

    /**
     * Formata número decimal 2 dígitos
     *
     * @return DecimalFormat
     */
    public static DecimalFormat decimalFormat2Digits() {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getNumberInstance(Locale.US);
        df.setGroupingUsed(false);
        df.setMinimumFractionDigits(2);
        df.setMaximumFractionDigits(2);
        return df;
    }

    /**
     * Formata número decimal 4 dígitos
     *
     * @return DecimalFormat
     */
    public static DecimalFormat decimalFormat4Digits() {
        DecimalFormat df = (DecimalFormat) DecimalFormat.getNumberInstance(Locale.US);
        df.setGroupingUsed(false);
        df.setMinimumFractionDigits(4);
        df.setMaximumFractionDigits(4);
        return df;
    }

    /**
     * Preenche valores a direita
     *
     * @param input
     * @param width
     * @param ch
     * @return String
     */
    public static String rightPad2(Object input, int width, char ch) {

        StringBuilder sb = new StringBuilder(String.valueOf(input).trim());

        while (sb.length() < width) {
            sb.insert(sb.length(), ch);
        }

        String strPad = sb.toString();

        if (strPad.length() > width) {
            strPad = strPad.substring(0, width);
        }

        return strPad;
    }

    /**
     * Preenche valores a esquerda
     *
     * @param input
     * @param width
     * @param ch
     * @return String
     */
    public static String leftPad2(Object input, int width, char ch) {

        StringBuilder sb = new StringBuilder(String.valueOf(input).trim());

        while (sb.length() < width) {
            sb.insert(0, ch);
        }

        String strPad = sb.toString();

        if (strPad.length() > width) {
            strPad = strPad.substring(0, width);
        }

        return strPad;
    }

    /**
     * Unmarshal XML data from the specified file and return the resulting
     * content tree.
     *
     * @param <T>
     * @param model
     * @param xml
     * @return
     * @throws JAXBException
     */
    public static <T> T unmarshaller(Class<? extends T> model, String xml) throws JAXBException {
        return (T) JAXBContext.newInstance(model).createUnmarshaller().unmarshal(new ByteArrayInputStream(xml.getBytes()));
    }

    /**
     * Marshal the content tree rooted at jaxbElement into SAX2 events.
     *
     * @param obj
     * @return
     * @throws JAXBException
     * @throws java.io.IOException
     */
    public static String marshaller(Object obj) throws JAXBException, IOException {

        Class model = obj.getClass();

        if (!model.isAnnotationPresent(XmlRootElement.class)) {
            throw new JAXBException("Anotação @XmlRootElement(name=\"***default***\") não definida na classe [" + obj.getClass() + "].");
        }

        XmlRootElement qName = (XmlRootElement) model.getAnnotation(XmlRootElement.class);

        Marshaller marshaller = JAXBContext.newInstance(model).createMarshaller();
        //marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        
        JAXBElement element = new JAXBElement(QName.valueOf(qName.name()), model, obj);

        ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
        try (BufferedOutputStream buff = new BufferedOutputStream(byteArray)) {
            marshaller.marshal(element, buff);
            buff.flush();
        }

        String xml = new String(byteArray.toByteArray());

        xml = xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", "");
        xml = xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", "");
        xml = xml.replace(" xmlns:ns2=\"http://www.w3.org/2000/09/xmldsig#\"", "");
        xml = xml.replace(" xmlns:ns3=\"http://www.w3.org/2000/09/xmldsig#\"", "");
        xml = xml.replace("ns2:", "");
        xml = xml.replace(":ns2", "");
        xml = xml.replace("ns3:", "");
        xml = xml.replace(":ns3", "");
        xml = xml.replace("<Signature>", "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">");

        return xml;
    }

    /**
     * Converte documento para string
     *
     * @param document
     * @return
     * @throws Exception
     */
    public static String doc2Str(org.w3c.dom.Document document) throws Exception {

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();

        trans.transform(new DOMSource(document), new StreamResult(os));

        String xml = os.toString();
        xml = xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
        xml = xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", "");
        xml = xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", "");
        xml = xml.replace("<Signature>", "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">");

        return xml;
    }

    /**
     * Converte string para documento
     *
     * @param str
     * @return
     * @throws Exception
     */
    public static org.w3c.dom.Document str2Doc(String str) throws Exception {
        DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
        builder.setNamespaceAware(true);
        return builder.newDocumentBuilder().parse(new ByteArrayInputStream(str.getBytes()));
    }

    /**
     * Limpa acentos
     *
     * @param text
     * @param upperCase
     * @return
     */
    public static String ascii(String text, boolean upperCase) {
        return Normalizer.normalize(upperCase ? text.toUpperCase() : text, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
    }

    /**
     * Transforma 'inputstream' em texto
     *
     * @param inputStream
     * @return String
     *
     * @throws IOException
     */
    public static String inputStream2String(InputStream inputStream) throws IOException {

        StringBuilder str = new StringBuilder("");

        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream), 1)) {

            String line;

            while ((line = bufferedReader.readLine()) != null) {
                str.append(line).append("\n");
            }
        }

        return str.toString().isEmpty() ? null : str.toString();
    }

    /**
     * Cria um arquivo novo
     *
     * @param path Caminho
     * @param content Conteúdo
     * @return
     * @throws IOException
     */
    public static File writeFile(String path, String content) throws IOException {
        return writeFile(new File(path), content, false, false);
    }

    /**
     * Cria um arquivo
     *
     * @param path Caminho
     * @param content Conteúdo
     * @param append Concatenar
     * @return
     * @throws IOException
     */
    public static File writeFile(String path, String content, boolean append) throws IOException {
        return writeFile(new File(path), content, append, false);
    }

    /**
     * Cria um arquivo
     *
     * @param path Caminho
     * @param content Conteúdo
     * @param append Concatenar
     * @param appendIni No início do conteúdo
     * @return
     * @throws IOException
     */
    public static File writeFile(String path, String content, boolean append, boolean appendIni) throws IOException {
        return writeFile(new File(path), content, append, appendIni);
    }

    /**
     * Cria um arquivo novo
     *
     * @param file Arquivo
     * @param content Conteúdo
     * @return
     * @throws IOException
     */
    public static File writeFile(File file, String content) throws IOException {
        return writeFile(file, content, false, false);
    }

    /**
     * Cria um arquivo
     *
     * @param file Arquivo
     * @param content Conteúdo
     * @param append Concatenar
     * @return
     * @throws IOException
     */
    public static File writeFile(File file, String content, boolean append) throws IOException {
        return writeFile(file, content, append, false);
    }

    /**
     * Cria um arquivo
     *
     * @param file Arquivo
     * @param content Conteúdo
     * @param append Concatenar
     * @param appendIni No início do conteúdo
     * @return
     * @throws IOException
     */
    public static File writeFile(File file, String content, boolean append, boolean appendIni) throws IOException {

        if (file.getParentFile() != null && !file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        if (!file.exists()) {
            file.createNewFile();
        }

        if (appendIni) {
            String str = readFile(file);
            content += "\n" + str;
            append = false;
        }

        try (PrintWriter p = new PrintWriter(new FileWriter(file, append))) {
            p.write(content);
        }

        return file;
    }

    /**
     * Lê um arquivo
     *
     * @param path Caminho
     * @return String Conteúdo do arquivo
     * @throws IOException
     */
    public static String readFile(String path) throws IOException {
        return readFile(new File(path));
    }

    /**
     * Lê um arquivo
     *
     * @param file Arquivo
     * @return String Conteúdo do arquivo
     * @throws IOException
     */
    public static String readFile(File file) throws IOException {

        if (file == null || !file.exists()) {
            throw new IOException("Arquivo não encontrado.");
        }

        StringBuilder str = new StringBuilder();

        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {

            String line;

            while ((line = bufferedReader.readLine()) != null) {
                str.append(line);
                str.append("\n");
            }
        }

        return str.toString().isEmpty() ? null : str.toString().trim();
    }

    public static byte[] fileToByte(String path) throws IOException {
        return fileToByte(new File(path));
    }

    public static byte[] fileToByte(File file) throws IOException {
        return Files.readAllBytes(file.toPath());
    }

    /**
     * Limita tamanho das palavras no texto
     *
     * @param text
     * @param numCharsWord
     * @return
     */
    public static String limitWords(String text, int numCharsWord) {

        String[] arr = text.split(" ");

        String str = "";

        for (String s : arr) {
            if (s.length() > numCharsWord) {
                str += s.substring(0, 3) + " ";
            } else {
                str += s + " ";
            }
        }

        return str;
    }

    /**
     * Endereço IP
     *
     * @return String IP
     */
    public static String getMyIP() {

        try {

            Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();

            for (NetworkInterface netint : Collections.list(nets)) {

                if (!netint.isLoopback() && netint.isUp()) {

                    Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();

                    for (InetAddress inetAddress : Collections.list(inetAddresses)) {
                        if (inetAddress.isSiteLocalAddress()) {
                            return inetAddress.getHostAddress();
                        }
                    }
                }
            }

        } catch (SocketException ex) {
            System.err.println(ex);
        }

        return null;
    }

    /**
     * Cria um clone de objeto
     *
     * @param obj Objeto Serializable
     * @return Object
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object clone(Object obj) throws IOException, ClassNotFoundException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(obj);
            oos.flush();
        }

        byte[] bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();
    }

    /**
     *
     * @param glue
     * @param arr
     * @return
     */
    public static String implode(String glue, String... arr) {
        return implode(glue, Arrays.asList(arr));
    }

    /**
     *
     * @param glue
     * @param arr
     * @return
     */
    public static String implode(String glue, List arr) {
        String str = "";
        for (int i = 0; i < arr.size(); i++) {
            str += arr.size() == (i + 1) ? arr.get(i) : arr.get(i) + glue;
        }
        return str;
    }

    /**
     * Verifica se contém valor numa determinada lista
     *
     * @param value
     * @param list
     * @return int Index do list e -1 para não contém
     */
    public static int contain(Object value, Object[] list) {

        if (value == null) {
            return -1;
        }

        for (int i = 0; i < list.length; i++) {
            Object obj = list[i];
            if (obj != null && obj.toString().equalsIgnoreCase(value.toString())) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Retorna todos arquivo incluido dentro do diretorio e subdiretorios
     *
     * @param path Caminho raiz
     * @param endsWith Extensao do arquivo para filtrar
     *
     * @return java.util.List
     *
     */
    public static final List<File> listFiles(String path, final String endsWith) {

        final List<File> files = new ArrayList();

        try {

            File file = new File("src");

            Files.walkFileTree(file.toPath(), new SimpleFileVisitor() {

                @Override
                public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {

                    if (attrs.isRegularFile() && file.toString().endsWith(endsWith)) {
                        files.add(new File(file.toString()));
                    }

                    return FileVisitResult.CONTINUE;
                }
            });

        } catch (IOException ex) {
            System.err.println(ex);
        }

        return files;
    }

    /**
     * Limpa caracteres telefone
     *
     * @param value
     * @return
     */
    public static String cleanPhone(String value) {

        String str = "";

        if (value == null) {
            return null;
        }

        String[] aux = value.split("/");

        if (aux.length == 1) {
            aux = value.split(" - ");
        }

        for (char c : aux[0].toCharArray()) {
            if (Character.isDigit(c)) {
                str += c;
            }
        }

        if (str.equals("0000000000")) {
            return null;
        }

        if (str.startsWith("051") || str.startsWith("055") || str.startsWith("053") || str.startsWith("054") || str.startsWith("052")) {
            str = str.substring(1);
        }

        if (str.length() == 8 && (str.startsWith("9") || str.startsWith("8"))) {
            str = "519" + str;
        }

        if (str.length() == 8) {
            str = "51" + str;
        }

        if (str.length() == 9) {
            str = "51" + str;
        }

        return str;
    }

    public static FontMetrics getFontMetrics() {
        return TEXT_FIELD.getFontMetrics(TEXT_FIELD.getFont());
    }
}
