/*
 * 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 com.arrebol.taxiservicios.beans;

import com.arrebol.taxiservicios.controller.util.DateWrapper;
import com.arrebol.taxiservicios.model.admin.Bitacora;
import com.arrebol.taxiservicios.model.admin.Taxi;
import com.arrebol.taxiservicios.model.core.Address;
import com.arrebol.taxiservicios.model.core.ContactNumber;
import com.arrebol.taxiservicios.model.core.Person;
import com.arrebol.taxiservicios.model.core.User;
import com.arrebol.taxiservicios.model.enums.PersonType;
import com.arrebol.taxiservicios.wrapper.PersonWrapper;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

/**
 * Taxi Servicios Tepic Generic Bean. All Beans are extended from this class
 * that contains access to the current user, external context, facescontext,
 * bundle property file.
 *
 * @author Picasso
 */
public abstract class TaxiGenericBean {

    /**
     * Check if currently user has the given role.
     *
     * @param role Role to be search.
     * @return true if user has given role otherwise false..
     */
    public synchronized boolean isUserInRoleBeanInBackground(String role) {
        FacesContext context = facesContext();
        ExternalContext externalContext = context.getExternalContext();

        return externalContext.isUserInRole(role);
    }

    public synchronized String getUserPrincipal() {
        FacesContext context = facesContext();
        ExternalContext externalContext = context.getExternalContext();

        return externalContext.getUserPrincipal().getName();
    }

    /**
     *
     * @return
     */
    public synchronized User getLoggedUser() {
        return (User) externalContext().getSessionMap().get(TAXI_SERVICIOS);
    }

    /**
     *
     * @param user
     */
    protected synchronized void setLoggedUser(User user) {
        externalContext().getSessionMap().put(TAXI_SERVICIOS, user);
    }

    /**
     *
     * @return
     */
    protected synchronized ExternalContext externalContext() {
        return facesContext().getExternalContext();
    }

    /**
     *
     * @return
     */
    protected synchronized FacesContext facesContext() {
        return FacesContext.getCurrentInstance();
    }

    /**
     * Wraper a Person from DB into a Customer, Provider, Employee or Driver.
     *
     * @param ps Person instance of Customer, Provider, Employee or Driver.
     *
     * @return Wraper list of Customer, Provider, Employee or Driver.
     */
    protected List<PersonWrapper> wraperPerson(List<Person> ps) {
        List<PersonWrapper> personWrapers = new ArrayList<>();

        ps.forEach((p) -> {
            PersonWrapper personWraper = new PersonWrapper(p.getId(), p.getFirstName(),
                    p.getSecondName(), p.getLastName(), p.getMiddleName(), p.getAdmissionDate());

            if (null != p.getAddresses() && !p.getAddresses().isEmpty()) {
                ((Set<Address>) p.getAddresses()).forEach((addrss) -> {
                    switch (addrss.getAddressType()) {
                        case HOME:
                            personWraper.setAddress(addrss.getAddressGoogleMap());
                            break;
                    }
                });
            }

            if (null != p.getContactNumbers() && !p.getContactNumbers().isEmpty()) {
                ((Set<ContactNumber>) p.getContactNumbers()).forEach((phone) -> {
                    switch (phone.getPhoneType()) {
                        case HOME:
                            personWraper.setLocalPhone(phone.getPhone());
                            break;
                        case MOBILE:
                            personWraper.setMobile(phone.getPhone());
                            break;
                    }
                });
            }

            if (p.getPersonType().equals(PersonType.CUSTOMER)
                    && null != p.getCustomerTaxis()
                    && !p.getCustomerTaxis().isEmpty()) {
                StringBuilder builder = new StringBuilder();
                Float totalAdeudo = 0F;

                ((Set<Taxi>) p.getCustomerTaxis()).forEach((Taxi taxi) -> {
                    builder.append(taxi.getEconomicNumber());
                    builder.append(", ");

                });

                for (Object taxi : p.getCustomerTaxis()) {
                    Taxi temp = (Taxi) taxi;
                    totalAdeudo += temp.getAdeudo();
                }
                personWraper.setAdeudoByCustomer(totalAdeudo);
                personWraper.setTaxisByCustomer(
                        builder.substring(
                                0,
                                builder.length() - 2
                        )
                );
            }

            personWrapers.add(personWraper);
        });

        return personWrapers;
    }

    /**
     *
     * @param person
     * @return
     */
    protected PersonWrapper wraperPerson(Person person) {
        PersonWrapper personWraper = new PersonWrapper(person.getId(), person.getFirstName(),
                person.getSecondName(), person.getLastName(), person.getMiddleName(), person.getAdmissionDate());

        if (null != person.getAddresses() && !person.getAddresses().isEmpty()) {
            ((Set<Address>) person.getAddresses()).forEach((addrss) -> {
                switch (addrss.getAddressType()) {
                    case HOME:
                        personWraper.setAddress(addrss.getAddressGoogleMap());
                        break;
                }
            });
        }

        return personWraper;
    }

    /**
     *
     * @param severity
     * @param title
     * @param details
     */
    protected void showMessage(FacesMessage.Severity severity, String title, String details) {
        facesContext().addMessage(null, new FacesMessage(severity, title, details));
    }
    

    /**
     * Validate business rules.
     *
     * @param pwd original Password
     * @param confirmPWd password to confirmed
     * @return true is password is valid otherwise false
     */
    protected boolean validatePasswordRules(String pwd, String confirmPWd) {
        boolean isValidPassword = false;

        if (null != pwd && null != confirmPWd
                && pwd.length() > 5) {
            isValidPassword = true;
        }

        return isValidPassword;
    }

    /**
     * Setup start date and end date.
     *
     * @param init True if comes from @PostConstruct otherwise false.
     */
    protected void setGenericUpDates(boolean init) {
        Calendar startCalendar = Calendar.getInstance();
        Calendar endCalendar = Calendar.getInstance();

        if (!init) {
            startCalendar.setTime(getGenericStartDate());
            endCalendar.setTime(getGenericEndDate());
        } else {
            startCalendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            endCalendar.setTime(new Date());
        }

        startCalendar.add(Calendar.HOUR_OF_DAY, 2);
        startCalendar.add(Calendar.MINUTE, 0);
        startCalendar.add(Calendar.SECOND, 0);
        startCalendar.add(Calendar.MILLISECOND, 0);

        endCalendar.add(Calendar.HOUR_OF_DAY, 22);
        endCalendar.add(Calendar.MINUTE, 59);
        endCalendar.add(Calendar.SECOND, 59);
        endCalendar.add(Calendar.MILLISECOND, 0);

        setGenericStartDate(startCalendar.getTime());
        setGenericEndDate(endCalendar.getTime());
    }

    protected void initGenericDates() {
        try {
            Calendar start = Calendar.getInstance();

            start.set(Calendar.WEEK_OF_YEAR, start.get(Calendar.WEEK_OF_YEAR) - 1);
            start.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

            setGenericStartDate(start.getTime());
            setGenericEndDate(DateWrapper.getTodayMXTime());
        } catch (Exception e) {

        }
    }

    protected static final String APP = "CarDriver";

    protected static final String TAXI_SERVICIOS = "carDriverAuth";
    private ResourceBundle bundlePropertyFile;
    private ResourceBundle bundleTaxiFile;
    private final String PROPERTY_FILE = "com.arrebol.taxiservicios.i18n.taxi_background";
    private final String PROPERTY_TAXI_FILE = "com.arrebol.taxiservicios.i18n.taxi";
    private Date today;
    private Date fiveLastDays;
    protected final Double iva = .16;
    private Date genericStartDate;
    private Date genericEndDate;

    public ResourceBundle getBundleTaxiFile() {
        return bundleTaxiFile;
    }

    public void loadTaxiPropertyFile() {
        this.bundleTaxiFile = ResourceBundle.getBundle(PROPERTY_TAXI_FILE);
    }
    
    

    public ResourceBundle getBundlePropertyFile() {
        return bundlePropertyFile;
    }

    public void loadBundlePropertyFile() {
        this.bundlePropertyFile = ResourceBundle.getBundle(PROPERTY_FILE);
    }

    public Date getToday() {
        if (null == today) {
            today = new Date();
        }
        return today;
    }

    public Date getFiveLastDays() {
        Calendar calendar = Calendar.getInstance();

        calendar.add(Calendar.DAY_OF_YEAR, 5);

        fiveLastDays = calendar.getTime();

        return fiveLastDays;
    }

    public void setToday(Date today) {
        this.today = today;
    }

    public String getCurrentYear() {
        Calendar calendar = Calendar.getInstance();

        return "" + calendar.get(Calendar.YEAR);
    }

    public Date getGenericStartDate() {
        return genericStartDate;
    }

    public void setGenericStartDate(Date genericStartDate) {
        this.genericStartDate = genericStartDate;
    }

    public Date getGenericEndDate() {
        return genericEndDate;
    }

    public void setGenericEndDate(Date genericEndDate) {
        this.genericEndDate = genericEndDate;
    }

    /**
     *
     * @param id
     * @param action
     * @param comments
     * @param desc
     * @return
     */
    public Bitacora createNewRecordInBitacora(String id, String action, String comments, String desc) {
        return new Bitacora(id, comments, action, desc, getLoggedUser().getUserName(), getLoggedUser(), DateWrapper.getTodayMXTime());
    }

    /**
     *
     * @param number Must be numeric (BigDecimal, Double, Float or Integer)
     * @return
     */
    public String currencyFormatNumber(Object number) {
        String currency;
        try {
            NumberFormat defaultFormat = NumberFormat.getCurrencyInstance();
            currency = defaultFormat.format(number);
        } catch (Exception e) {
            currency = "$0.00";
        }
        return currency;
    }

    Integer counter = 0;
    String value = "";

    private final String[] UNIDADES = {"", "un ", "dos ", "tres ", "cuatro ", "cinco ", "seis ", "siete ", "ocho ", "nueve "};
    private final String[] DECENAS = {"diez ", "once ", "doce ", "trece ", "catorce ", "quince ", "dieciseis ",
        "diecisiete ", "dieciocho ", "diecinueve", "veinte ", "treinta ", "cuarenta ",
        "cincuenta ", "sesenta ", "setenta ", "ochenta ", "noventa "};
    private final String[] CENTENAS = {"", "ciento ", "doscientos ", "trecientos ", "cuatrocientos ", "quinientos ", "seiscientos ",
        "setecientos ", "ochocientos ", "novecientos "};

    public String Convertir(String numero, boolean mayusculas) {
        String literal = "";
        String parte_decimal;
        //si el numero utiliza (.) en lugar de (,) -> se reemplaza
        numero = numero.replace(".", ",");
        //si el numero no tiene parte decimal, se le agrega ,00
        if (numero.indexOf(",") == -1) {
            numero = numero + ",00";
        }
        //se valida formato de entrada -> 0,00 y 999 999 999,00
        if (Pattern.matches("\\d{1,9},\\d{1,2}", numero)) {
            //se divide el numero 0000000,00 -> entero y decimal
            String Num[] = numero.split(",");
            //de da formato al numero decimal
            parte_decimal = "pesos " + Num[1] + "/100 MXN.";
            //se convierte el numero a literal
            if (Integer.parseInt(Num[0]) == 0) {//si el valor es cero
                literal = "cero ";
            } else if (Integer.parseInt(Num[0]) > 999999) {//si es millon
                literal = getMillones(Num[0]);
            } else if (Integer.parseInt(Num[0]) > 999) {//si es miles
                literal = getMiles(Num[0]);
            } else if (Integer.parseInt(Num[0]) > 99) {//si es centena
                literal = getCentenas(Num[0]);
            } else if (Integer.parseInt(Num[0]) > 9) {//si es decena
                literal = getDecenas(Num[0]);
            } else {//sino unidades -> 9
                literal = getUnidades(Num[0]);
            }
            //devuelve el resultado en mayusculas o minusculas
            if (mayusculas) {
                return (literal + parte_decimal).toUpperCase();
            } else {
                return (literal + parte_decimal);
            }
        } else {//error, no se puede convertir
            return literal = null;
        }
    }

    /* funciones para convertir los numeros a literales */
    private String getUnidades(String numero) {// 1 - 9
        //si tuviera algun 0 antes se lo quita -> 09 = 9 o 009=9
        String num = numero.substring(numero.length() - 1);
        return UNIDADES[Integer.parseInt(num)];
    }

    private String getDecenas(String num) {// 99                        
        int n = Integer.parseInt(num);
        if (n < 10) {//para casos como -> 01 - 09
            return getUnidades(num);
        } else if (n > 19) {//para 20...99
            String u = getUnidades(num);
            if (u.equals("")) { //para 20,30,40,50,60,70,80,90
                return DECENAS[Integer.parseInt(num.substring(0, 1)) + 8];
            } else {
                return DECENAS[Integer.parseInt(num.substring(0, 1)) + 8] + "y " + u;
            }
        } else {//numeros entre 11 y 19
            return DECENAS[n - 10];
        }
    }

    private String getCentenas(String num) {// 999 o 099
        if (Integer.parseInt(num) > 99) {//es centena
            if (Integer.parseInt(num) == 100) {//caso especial
                return " cien ";
            } else {
                return CENTENAS[Integer.parseInt(num.substring(0, 1))] + getDecenas(num.substring(1));
            }
        } else {//por Ej. 099 
            //se quita el 0 antes de convertir a decenas
            return getDecenas(Integer.parseInt(num) + "");
        }
    }

    private String getMiles(String numero) {// 999 999
        //obtiene las centenas
        String c = numero.substring(numero.length() - 3);
        //obtiene los miles
        String m = numero.substring(0, numero.length() - 3);
        String n = "";
        //se comprueba que miles tenga valor entero
        if (Integer.parseInt(m) > 0) {
            n = getCentenas(m);
            return n + "mil " + getCentenas(c);
        } else {
            return "" + getCentenas(c);
        }

    }

    private String getMillones(String numero) { //000 000 000        
        //se obtiene los miles
        String miles = numero.substring(numero.length() - 6);
        //se obtiene los millones
        String millon = numero.substring(0, numero.length() - 6);
        String n = "";
        if (millon.length() > 1) {
            n = getCentenas(millon) + "millones ";
        } else {
            n = getUnidades(millon) + "millon ";
        }
        return n + getMiles(miles);
    }

    public String getStringOfNumber(Integer $num) {
        this.counter = $num;
        return doThings($num);
    }

    private String doThings(Integer _counter) {
        //Limite
        if (_counter > 2000000) {
            return "DOS MILLONES";
        }

        switch (_counter) {
            case 0:
                return "CERO";
            case 1:
                return "UN"; //UNO
            case 2:
                return "DOS";
            case 3:
                return "TRES";
            case 4:
                return "CUATRO";
            case 5:
                return "CINCO";
            case 6:
                return "SEIS";
            case 7:
                return "SIETE";
            case 8:
                return "OCHO";
            case 9:
                return "NUEVE";
            case 10:
                return "DIEZ";
            case 11:
                return "ONCE";
            case 12:
                return "DOCE";
            case 13:
                return "TRECE";
            case 14:
                return "CATORCE";
            case 15:
                return "QUINCE";
            case 20:
                return "VEINTE";
            case 30:
                return "TREINTA";
            case 40:
                return "CUARENTA";
            case 50:
                return "CINCUENTA";
            case 60:
                return "SESENTA";
            case 70:
                return "SETENTA";
            case 80:
                return "OCHENTA";
            case 90:
                return "NOVENTA";
            case 100:
                return "CIEN";

            case 200:
                return "DOSCIENTOS";
            case 300:
                return "TRESCIENTOS";
            case 400:
                return "CUATROCIENTOS";
            case 500:
                return "QUINIENTOS";
            case 600:
                return "SEISCIENTOS";
            case 700:
                return "SETECIENTOS";
            case 800:
                return "OCHOCIENTOS";
            case 900:
                return "NOVECIENTOS";

            case 1000:
                return "MIL";

            case 1000000:
                return "UN MILLON";
            case 2000000:
                return "DOS MILLONES";
        }
        if (_counter < 20) {
            //System.out.println(">15");
            return "DIECI" + doThings(_counter - 10);
        }
        if (_counter < 30) {
            //System.out.println(">20");
            return "VEINTI" + doThings(_counter - 20);
        }
        if (_counter < 100) {
            //System.out.println("<100"); 
            return doThings((int) (_counter / 10) * 10) + " Y " + doThings(_counter % 10);
        }
        if (_counter < 200) {
            //System.out.println("<200"); 
            return "CIENTO " + doThings(_counter - 100);
        }
        if (_counter < 1000) {
            //System.out.println("<1000");
            return doThings((int) (_counter / 100) * 100) + " " + doThings(_counter % 100);
        }
        if (_counter < 2000) {
            //System.out.println("<2000");
            return "MIL " + doThings(_counter % 1000);
        }
        if (_counter < 1000000) {
            String var = "";
            //System.out.println("<1000000");
            var = doThings((int) (_counter / 1000)) + " MIL";
            if (_counter % 1000 != 0) {
                //System.out.println(var);
                var += " " + doThings(_counter % 1000);
            }
            return var;
        }
        if (_counter < 2000000) {
            return "UN MILLON " + doThings(_counter % 1000000);
        }
        return "";
    }
    
    public static String formatearDecimales(Double numero) {
        DecimalFormat formato = new DecimalFormat("0.00");
        return formato.format(numero);

    }
    
     public static String formatearDecimales(float numero) {
        DecimalFormat formato = new DecimalFormat("0.00");
        return formato.format(numero);

    }
}
