/*
 *
 * Copyright (c) 2003 Tienshiao Ma
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal 
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 * SOFTWARE.
 */
package SOMCalc;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

import net.jscience.util.MathFP;
/**
 *
 * @author  Tienshiao Ma
 * @version
 */
public class SOMCalc extends MIDlet implements CommandListener {
    private String Radios[] = { "Generic", "Senao 2511", "Orinoco", "Cisco" };
    private int RadioSpecs[][] = { {   0,   0,   0,   0 },     // Generic
                                   { -89, -91, -93, -95 },     // Senao
                                   { -82, -87, -91, -94 },     // Orinoco
                                   { -85, -89, -91, -94 } };   // Cisco
    private String Speeds[] = { "11", "5.5", "2", "1" };
                                   
    private Command exitCommand; // The exit command
    private Command backCommand;
    private Command calcCommand;
    private Display display;    // The display for this MIDlet
    
    private Form main;    
    private TextField tfMHz;
    private TextField tfMiles;
    private TextField tfTxPower;
    private TextField tfTxGain;
    private TextField tfRxGain;
    private ChoiceGroup cgAgency;
    private ChoiceGroup cgAnalysis;
    private ChoiceGroup cgLinkType;
    private ChoiceGroup cgRadio;
    
    private Form results;
    
    public SOMCalc() {
        display = Display.getDisplay(this);
        exitCommand = new Command("Exit", Command.EXIT, 2);

        main = new Form("SOM Calculator");
        
        tfMHz = new TextField("MHz:", "2400", 10, TextField.NUMERIC);
        tfMiles = new TextField("Miles:", "", 10, TextField.NUMERIC);        
        tfTxPower = new TextField("Tx Power:", "", 10, TextField.NUMERIC);
        tfTxGain = new TextField("Tx Gain:", "", 10, TextField.NUMERIC);
        tfRxGain = new TextField("Rx Gain:", "", 10, TextField.NUMERIC);
        main.append(tfMHz);
        main.append(tfMiles);        
        main.append(tfTxPower);
        main.append(tfTxGain);
        main.append(tfRxGain);
        
        cgAgency = new ChoiceGroup("Regulatory Agency:", Choice.EXCLUSIVE);
        cgAgency.append("FCC", null);
        cgAgency.append("none", null);
        main.append(cgAgency);
                
        cgLinkType = new ChoiceGroup("Link Type:", Choice.EXCLUSIVE);
        cgLinkType.append("P2P", null);
        cgLinkType.append("P2MP", null);
        main.append(cgLinkType);

        cgAnalysis = new ChoiceGroup("Expert Analysis?", Choice.EXCLUSIVE);
        cgAnalysis.append("Yes", null);
        cgAnalysis.append("No", null);
        main.append(cgAnalysis);
        
        cgRadio = new ChoiceGroup("Radio:", Choice.EXCLUSIVE);
        for (int i = 0; i < Radios.length; i++) {
            cgRadio.append(Radios[i], null);
        }
        main.append(cgRadio);
        
        
        calcCommand = new Command("Calc", Command.SCREEN, 2);        
        main.addCommand(exitCommand);
        main.addCommand(calcCommand);
        main.setCommandListener(this);        
    }
    
    public void startApp() {        
        display.setCurrent(main);
    }
    
    public void pauseApp() {
    }
    
    /**
     * Destroy must cleanup everything not handled by the garbage collector.
     * In this case there is nothing to cleanup.
     */
    public void destroyApp(boolean unconditional) {
    }
    
    /*
     * Respond to commands, including exit
     * On the exit command, cleanup and notify that the MIDlet has been destroyed.
     */
    public void commandAction(Command c, Displayable s) {
        if (c == exitCommand) {
            destroyApp(false);
            notifyDestroyed();
        } else if (c == calcCommand) {
            
            int fixedPathLoss;
            int fixedSignal;
            int EIRP;
            int txGain;
            try {
                int fixedFreq = MathFP.toFP(tfMHz.getString());
                int fixedMiles = MathFP.toFP(tfMiles.getString());
                
                // $pathloss = 20 * log10($freq) + 20 * log10($miles) + 36.6;
                //           = 20 * (log10($freq) + log10($miles)) + 36.6;
                //           = 20 * (ln($freq)/ln(10) + ln($miles)/ln(10)) + 36.6;
                fixedPathLoss = 20 * (MathFP.div(MathFP.log(fixedFreq), MathFP.log(MathFP.toFP(10))) + 
                                     MathFP.div(MathFP.log(fixedMiles), MathFP.log(MathFP.toFP(10)))) + MathFP.toFP("36.6");
                
                // $signal = $txpower + $txgain - $pathloss + $rxgain;
                fixedSignal = MathFP.toFP(tfTxPower.getString()) + 
                                MathFP.toFP(tfTxGain.getString()) -
                                fixedPathLoss +
                                MathFP.toFP(tfRxGain.getString());
                
                txGain = Integer.parseInt(tfTxPower.getString());
                EIRP = txGain + Integer.parseInt(tfTxGain.getString());
            } catch (NumberFormatException nfe) {
                return;
            }             

            results = new Form("SOM Results");           
            results.append(new StringItem("Rcv Signal:", MathFP.toString(fixedSignal, 1) + "dBm"));
            results.append(new StringItem("Path Loss:", MathFP.toString(fixedPathLoss, 1) + "dB"));
            results.append(new StringItem("Miles:", tfMiles.getString()));
            results.append(new StringItem("EIRP:", Integer.toString(EIRP) + "dBm"));
            
            if (cgAgency.getSelectedIndex() == 0) { // FCC
                results.append("\n");
                if (cgLinkType.getSelectedIndex() == 0) { // Point to Point
                    int maxEIRP = ((txGain - 6) / 3) * 2 + 36;
                    if (EIRP > maxEIRP) {
                        results.append("Exceeds FCC Regulations\n");
                    } else {
                        results.append("Meets FCC Regulations\n");
                    }
                } else { // Point to Multi-Point
                    if (EIRP > 36) {
                        results.append("Exceeds FCC Regulations\n");
                    } else {
                        results.append("Meets FCC Regulations\n");
                    }
                }                
            }
            
            if (cgAnalysis.getSelectedIndex() == 0) { // Yes to Expert Analysis
                results.append("--\n");                
                int radio = cgRadio.getSelectedIndex();                
                if (radio > 0) {
                    results.append("Expert Analysis\n");
                    results.append(new StringItem("Radio Type:", Radios[radio]));
                    results.append("\n");
                    
                    for (int i = 0; i < 4; i++) {
                        results.append(Speeds[i] + "mbps (" + RadioSpecs[radio][i] + ")\n");

                        int fixedMargin = fixedSignal - MathFP.toFP(RadioSpecs[radio][i]);
                        if (MathFP.max(fixedMargin, 0) == fixedMargin) {
                            results.append(new StringItem("Link:", "Yes"));
                        } else {
                            results.append(new StringItem("Link:", "No"));
                        }
                        results.append(new StringItem("Margin:", MathFP.toString(fixedMargin, 1) + " dB"));
                        int intMargin = MathFP.toInt(fixedMargin);
                        if (intMargin < 8) {
                            results.append(new StringItem("Reliability:", "Unreliable"));
                            results.append(new StringItem("Uptime:", "(<90%)"));
                        } else if (intMargin < 18) {
                            results.append(new StringItem("Reliability:", "Poor"));
                            results.append(new StringItem("Uptime:", "(90%-99%)"));
                        } else if (intMargin < 28) {
                            results.append(new StringItem("Reliability:", "Fair"));
                            results.append(new StringItem("Uptime:", "(99%-99.9%)"));
                        } else if (intMargin < 38) {
                            results.append(new StringItem("Reliability:", "Good"));
                            results.append(new StringItem("Uptime:", "(99.9%-99.99%)"));                        
                        } else if (intMargin < 48) {
                            results.append(new StringItem("Reliability:", "Excellent"));
                            results.append(new StringItem("Uptime:", "(99.99%-99.999%)"));
                        } else if (intMargin >= 48) {
                            results.append(new StringItem("Reliability:", "Solid"));
                            results.append(new StringItem("Uptime:" ,"(>99.999%)"));                        
                        }                         
                    }                    
                } else {
                    results.append("Expert Analysis requires a radio selection\n");
                }                
            }
            
            backCommand = new Command("Back", Command.SCREEN, 2);
            results.addCommand(exitCommand);
            results.addCommand(backCommand);
            results.setCommandListener(this);        
            display.setCurrent(results);

        } else if (c == backCommand) {
            display.setCurrent(main);
        }
    }
}
