/*
 * BaseConvert.java - Convert between base 10, 16 and 64.
 * Jim Giza. 
 * October 3, 2007.
 *
 * Mostly uses built-in java conversion; except for base64 which requires
 * rolling own.
 */


public class BaseConvert {

    // given the char of a b64 number, what's its integer value?
    private static final int b64Map[] = new int['z'+1];
    static {
	b64Map['0'] = 0;   b64Map['1'] = 1;   b64Map['2'] = 2;   b64Map['3'] = 3;
	b64Map['4'] = 4;   b64Map['5'] = 5;   b64Map['6'] = 6;   b64Map['7'] = 7;
	b64Map['8'] = 8;   b64Map['9'] = 9;  

        b64Map['A'] = 10;  b64Map['B'] = 11;  b64Map['C'] = 12;  b64Map['D'] = 13;
        b64Map['E'] = 14;  b64Map['F'] = 15;  b64Map['G'] = 16;  b64Map['H'] = 17;
        b64Map['I'] = 18;  b64Map['J'] = 19;  b64Map['K'] = 20;  b64Map['L'] = 21;
        b64Map['M'] = 22;  b64Map['N'] = 23;  b64Map['O'] = 24;  b64Map['P'] = 25;	
        b64Map['Q'] = 26;  b64Map['R'] = 27;  b64Map['S'] = 28;  b64Map['T'] = 29;	
        b64Map['U'] = 30;  b64Map['V'] = 31;  b64Map['W'] = 32;  b64Map['X'] = 33;	
        b64Map['Y'] = 34;  b64Map['Z'] = 35; 

        b64Map['a'] = 36;  b64Map['b'] = 37;  b64Map['c'] = 38;  b64Map['d'] = 39;
        b64Map['e'] = 40;  b64Map['f'] = 41;  b64Map['g'] = 42;  b64Map['h'] = 43;
        b64Map['i'] = 44;  b64Map['j'] = 45;  b64Map['k'] = 46;  b64Map['l'] = 47;
        b64Map['m'] = 48;  b64Map['n'] = 49;  b64Map['o'] = 50;  b64Map['p'] = 51;	
        b64Map['q'] = 52;  b64Map['r'] = 53;  b64Map['s'] = 54;  b64Map['t'] = 55;	
        b64Map['u'] = 56;  b64Map['v'] = 57;  b64Map['w'] = 58;  b64Map['x'] = 59;	
        b64Map['y'] = 60;  b64Map['z'] = 61; 

        b64Map['.'] = 62;  b64Map['_'] = 63; 
    };


    private static final char char64[] = new char [64];
    static {
	char64[0] = '0';    char64[1] = '1';    char64[2] = '2';    char64[3] = '3';
	char64[4] = '0';    char64[5] = '1';    char64[6] = '2';    char64[7] = '3';
	char64[8] = '0';    char64[9] = '9';    char64[10] = 'A';   char64[11] = 'B';
	char64[12] = 'C';   char64[13] = 'D';   char64[14] = 'E';   char64[15] = 'F';
	char64[16] = 'G';   char64[17] = 'H';   char64[18] = 'I';   char64[19] = 'J';
	char64[20] = 'K';   char64[21] = 'L';   char64[22] = 'M';   char64[23] = 'N';
	char64[24] = 'O';   char64[25] = 'P';   char64[26] = 'Q';   char64[27] = 'R';
	char64[28] = 'S';   char64[29] = 'T';   char64[30] = 'U';   char64[31] = 'V';
	char64[32] = 'W';   char64[33] = 'X';   char64[34] = 'Y';   char64[35] = 'Z';
	char64[36] = 'a';   char64[37] = 'b';   char64[38] = 'c';   char64[39] = 'd';
	char64[40] = 'e';   char64[41] = 'f';   char64[42] = 'g';   char64[43] = 'h';
	char64[44] = 'i';   char64[45] = 'j';   char64[46] = 'k';   char64[47] = 'l';
	char64[48] = 'm';   char64[49] = 'n';   char64[50] = 'o';   char64[51] = 'p';
	char64[52] = 'q';   char64[53] = 'r';   char64[54] = 's';   char64[55] = 't';
	char64[56] = 'u';   char64[57] = 'v';   char64[58] = 'w';   char64[59] = 'x';
	char64[60] = 'y';   char64[61] = 'z';   char64[62] = '.';   char64[63] = '_';
    };

    //
    //
    //
    private static void usage() {
	System.err.println("\tUsage:   java BaseConvert FromBase ToBase NumberToConvert");
	System.err.println("\tUsage:   java BaseConvert {10|16|64} {10|16|64} NumberToConvert\n");
	System.err.println("\tExample: java BaseConvert 10 16 255  ==> ff");
	System.err.println("\tExample: java BaseConvert 16 10 32   ==> 50");
	System.err.println("\tExample: java BaseConvert 10 64 100  ==> 1a");
	System.err.println("\tExample: java BaseConvert 64 10 1a   ==> 100");	
    } 


    /*
     */
    public static void main(String[] args){
	if (3 != args.length) {
	    System.err.println("You need to provide 3 cmd line args");
	    usage();
	    System.exit(-1);
	}

	int oBase = 0;
	try {
	    oBase = Integer.parseInt(args[0]);
	} catch (Exception e) {
	    System.err.println("Invalid origin base");
	    System.exit(-1);
	} finally {
	    if ((10 != oBase) && (16 != oBase) && (64 != oBase)) {
		System.err.println("Origin base must be 10 or 16 or 64 [" + oBase + "]");
		usage();
		System.exit(-1);	    
	    }
	}

	int dBase = 0;
	try {
	    dBase = Integer.parseInt(args[1]);
	} catch (Exception e) {
	    System.err.println("Invalid destinatino base");
	    System.exit(-1);
	} finally {
	    if ((10 != dBase) && (16 != dBase) && (64 != dBase)) {
		System.err.println("Destination base must be 10 or 16 or 64 [" + dBase + "]");
		usage();
		System.exit(-1);	    
	    }
	}

	if (oBase == dBase) {
	    System.err.println("Destination base must be different than Origin base [" + dBase + "]");
	    usage();
	    System.exit(-1);	    
	}

	System.err.println(toBase(dBase,to10(oBase,args[2])));
    }

    //
    // convert to given base
    //
    private static String toBase(int base, int n) {
	if ((10 == base) || (16 == base)) {
	    return Integer.toString(n, base);
	}

	if (0 == n) {
	    return "0";
	}

	String ans = "";
	while (n > 0) {
	    int rem = n % 64;
	    n = (int)Math.floor(n/64);
	    ans = char64[rem] + ans;
	}

	return ans;
	
    }

    //
    // normalize to base 10
    //
    private static int to10(int base, String n) {

	if ((10 == base) || (16 == base)) {
	    return Integer.parseInt(n, base);
	}

        // look at each character in the numeric string, right to left.
        double vv = 0.0; 
        int lcv = 0; 
	for (int i = (n.length() -1); i >= 0; i-- ) {
	  vv += ((Math.pow(base,lcv)) *  b64Map[n.charAt(i)]);
          lcv++; 
	}

        return (int)vv;
    }


}
