package com.utopiaindustries.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibm.icu.text.RuleBasedNumberFormat;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;

public class NumberUtils {

    /**
     * add padding of specified size to a number
     */
    public static String addPaddingZeros( long num, long paddingSize ) {
        return String.format( "%0" + paddingSize + "d", num );
    }

    /**
     * add a number into null value
     */
    public static Float addToNull( Float num1, float num2 ) {
        return ( num1 == null ) ? ( num2 ) : ( num1 + num2 );
    }

    /**
     * subtract a number from null value
     */
    public static Float subtractFromNull( Float num1, float num2 ) {
        return ( num1 == null ) ? ( 0 ) : ( num1 - num2 );
    }

    /**
     * check if null or zero
     */
    public static boolean isNullOrZero( Long number ) {
        return ( number == null || number == 0 );
    }

    public static boolean isNullOrZero( Integer number ) {
        return ( number == null || number == 0 );
    }

    /**
     * check if any not null or zero
     */
    public static boolean isAnyNotNullOrZero( Long... numbers ) {
        boolean isAnyNotNullOrZero = false;
        for ( Long number : numbers ) {
            isAnyNotNullOrZero = isAnyNotNullOrZero || ( ! isNullOrZero( number ) );
        }
        return isAnyNotNullOrZero;
    }

    /**
     * is any null
     */
    public static boolean isAnyNull( Long... numbers ) {
        // check if any null
        for ( Long number : numbers ) {
            if ( number == null ) {
                return true;
            }
        }
        return false;
    }

    /**
     * format decimal places upto given places
     */
    public static float formatDecimalPlace( double number, int places ) {
        int factor = ( int ) Math.pow( 10, places );
        return ( float ) Math.floor( number * factor ) / factor;
    }

    /**
     * round decimal and format upto given places
     */
    public static float roundAndFormatDecimal( double number, int places ) {
        int factor = ( int ) Math.pow( 10, places );
        return ( float ) Math.round( number * factor ) / factor;
    }

    //check if an object is null or empty
    public static boolean isNullOrEmpty(Object obj) {
        if (obj == null) {
            return true;
        } else
            return false;
    }

    //long to int
    public static int longToInt(long number) {
        return ( ( int ) number );
    }

    //Long list to Long array
    public static Long[] longListToLongArray( List<Long> longList ) {
        if( longList != null ) {
            Long[] longArray = new Long[ longList.size() ];
            return longList.toArray( longArray );
        }
        return new Long[0];
    }

    //Long Set to Long array
    public static Long[] longSetToLongArray( Set<Long> longSet ) {
        if( longSet != null ) {
            Long[] longArray = new Long[ longSet.size() ];
            return longSet.toArray( longArray );
        }
        return new Long[0];
    }

    // number to words
    public static String numberToWords( BigDecimal value ) {
        RuleBasedNumberFormat ruleBasedNumberFormat = new RuleBasedNumberFormat( new Locale( "EN", "US" ), RuleBasedNumberFormat.SPELLOUT );
        return ruleBasedNumberFormat.format( value );
    }

    /**
     * convert JSON to Long list
     */
    public static List<Long> JsonToLongList( String body ) {
        ObjectMapper objectMapper = new ObjectMapper();
        List<Long> longList = new ArrayList<Long>();

        //JSON to Long list
        try {
            longList = objectMapper.readValue( body, new TypeReference<List<Long>>(){} );
        } catch ( JsonProcessingException e ) {
            e.printStackTrace();
        }

        return longList;
    }

    /**
     * String to long
     */
    public static long stringToLong( String string ) {
        if( !StringUtils.isNullOrEmpty( string ) ) {
            try {
                return Long.parseLong( string );
            }
            catch( Exception e ) {
            }
        }
        return 0;
    }

    /**
     * String to wrapper long
     */
    public static Long stringToWrapperLong( String string ) {
        if( !StringUtils.isNullOrEmpty( string ) ) {
            try {
                return Long.parseLong( string );
            }
            catch( Exception e ) {
            }
        }
        return null;
    }

    /*
     *  is Negative number
     * */
    public static boolean isNegative (long number){ return number < 0 ; }

    public static String getCommaSeparatedNumberString( float number ) {
        String numberString = String.valueOf( number );
        StringBuilder formattedNumber = new StringBuilder( numberString );
        for( int i = numberString.indexOf( "." ) - 3; i > 0; i = i - 3 ) {
            formattedNumber.insert( i, "," );
        }
        // if string ends with '.0', delete '.0'
        if( formattedNumber.length() - 2 > 0 &&
                formattedNumber.charAt( formattedNumber.length() - 2 ) == '.' &&
                formattedNumber.charAt( formattedNumber.length() - 1 ) == '0' ) {
            formattedNumber.delete( formattedNumber.length() - 2, formattedNumber.length() );
        }
        return formattedNumber.toString();
    }

    /**
     * is valid long
     */
    public static boolean isValidLong( String code ) {
        try {
            Long.parseLong( code );
        } catch ( NumberFormatException e ) {
            return false;
        }
        return true;
    }

    //  truncate number upto decimal places
    public static float truncateNumber( float value , int places ){
        BigDecimal bd = new BigDecimal( Float.toString( value  ) );
        return bd.setScale( places, BigDecimal.ROUND_DOWN ).floatValue();
    }

    public static Float roundOffNumberToTwoPlaces( Float value ){
        if (value >= 0.0f && value < 1.0f && hasMoreThanTwoDecimalPlaces( value )) {
            value = 0.0f;
        } else {
            value = Math.round(value * 100.0f) / 100.0f;
        }
        if (value != 0.0f && value % 1 == 0) {
            value = Math.round(value * 10.0f) / 10.0f;
        }
        return value;
    }

    // Helper to check if the value has more than two decimal places
    private static boolean hasMoreThanTwoDecimalPlaces(float value) {
        float scaledValue = Math.abs(value) * 100.0f;
        return scaledValue != (float) Math.round(scaledValue);
    }
}