/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.text;

import java.io.IOException;
import net.ranides.assira.text.PrintParams.PrintFlags;

/**
 *
 * @author ranides
 * @todo (migration) assira (printf)
 */
public interface PrintFunction {
    
    void apply(Appendable target, PrintParams params, Object[] values) throws IOException;

    int estimate(PrintParams params, Object[] values);
    
    class PrintNumberFP implements PrintFunction {
        
        public static final PrintNumberFP INSTANCE = new PrintNumberFP();

        @Override
        public void apply(Appendable target, PrintParams params, Object[] values) throws IOException {
            double value    = Math.abs(((Number)values[params.index()]).doubleValue());
            int prec        = params.precision() > 0 ? params.precision() : 6;
            double n        = Math.floor(value);
            double f        = Math.round((value - n) * Math.pow(10, prec));
            String text     = ((int)n) + "." + ((int)f);

            PrintNumberDec.apply(target, params, text, value<0);
        }

        @Override
        public int estimate(PrintParams params, Object[] values) {
            Object value = values[params.index()];
            int digits = (int)Math.ceil(Math.log10(((Number)value).doubleValue()));
            return Math.max(params.width(), digits);
        }
        
    }
    
    class PrintNumberDec implements PrintFunction {
        
        public static final PrintNumberDec INSTANCE = new PrintNumberDec();

        @SuppressWarnings("PMD.NPathComplexity")
        static void apply(Appendable target, PrintParams params, String text, boolean negative) throws IOException {
            int dpadd = (params.width()-text.length());
            if( params.flags().contains(PrintFlags.SIGN_PADDING) || negative ) {
                dpadd -= 1;
            }
            boolean lpadd = dpadd>0 && params.flags().contains(PrintFlags.ALIGN_LEFT);
            boolean rpadd = dpadd>0 && params.flags().contains(PrintFlags.ALIGN_RIGHT);
            CharSequence padd = StringUtils.nCopies(dpadd, params.padder());
            
            if(rpadd && params.padder()!='0') {
                target.append(padd);
            }
            if(!negative) {
                if( params.flags().contains(PrintFlags.SIGN_SHOW) ) {
                    target.append('+');
                }
                else if( params.flags().contains(PrintFlags.SIGN_PADDING) ) {
                    target.append(params.padder());
                }
            } else {
                target.append('-');
            }
            if(rpadd && params.padder()=='0') {
                target.append(padd);
            }
            target.append(text);
            if(lpadd) {
                target.append(padd);
            }
        }

        @Override
        public void apply(Appendable target, PrintParams params, Object[] values) throws IOException {
            long value = Math.abs( ((Number)values[params.index()]).longValue() );
            apply(target, params, Long.toString(value), value<0);
        }

        @Override
        public int estimate(PrintParams params, Object[] values) {
            Object value = values[params.index()];
            int digits = (int)Math.ceil(Math.log10(((Number)value).doubleValue()));
            return Math.max(params.width(), digits);
        }
        
    }
    
    class PrintString implements PrintFunction {
        
        public static final PrintString INSTANCE = new PrintString();
        
        @Override
        public void apply(Appendable target, PrintParams params, Object[] values) throws IOException {
            String text = String.valueOf(values[params.index()]);
            if(params.width() > text.length()) {
                CharSequence padd = StringUtils.nCopies(params.width()-text.length(), params.padder());
                if(params.flags().contains(PrintParams.PrintFlags.ALIGN_LEFT)) {
                    target.append(text).append(padd);
                } 
                else if(params.flags().contains(PrintParams.PrintFlags.ALIGN_RIGHT)) {
                    target.append(padd).append(text);
                } else {
                    target.append(text);
                }
            } else {
                target.append(text);
            }
        }
        
        @Override
        public int estimate(PrintParams params, Object[] values) {
            return Math.max(params.width(), String.valueOf(values[params.index()]).length());
        }
        
    };
    
}
