// -*- Mode: Eagle -*-
/*
 *
 * Math utilities.
 *
 * Copyright 2007 - 2013 by John Johnson Software, LLC.
 * All Rights Reserved.
 *
 */

//#include "source/drill_sizes.h"
#include "drill_sizes.h"

real MM_PER_INCH    = 25.4;
int MICRONS_PER_MM  = 1000;
int MILS_PER_INCH   = 1000;

//
// Thanks EAGLE! No one expects an internal units change. :-)
//
int INTERNAL_SCALAR = 10000;
if (EAGLE_VERSION >= 6) {
  INTERNAL_SCALAR = 320000;
}

int INTERNALS_PER_MICRON = (INTERNAL_SCALAR / 1000);
int INTERNALS_PER_MM = INTERNAL_SCALAR;
int INTERNALS_PER_MIL = MM_PER_INCH * (INTERNAL_SCALAR / 1000);
int INTERNALS_PER_INCH = MM_PER_INCH * INTERNAL_SCALAR;

//
// Return the suffix unit of measure from a string.
//
// Params:
//  s   String containing the number with suffix.
// Returns:
//  string  Suffix of the string.
// Changes:
//  none
//
string get_units(string s)
{
  int len;
  string units;
  
  len = strlen(s);
  units = strsub(s, len - 2, 2);
  // special handling for wire size drill numbers
  if (strsub(units, 1, 1) == "#")
    units = "#";
  return units;
}  

//
// Convert from one unit of measure to another.
//
// Params:
//  value     Value to convert.
//  old_units Unit of measure value is currently in.
//  new_units Unit of measure to convert to.
// Returns:
//  real  Value converted to new unit of measure
// Changes:
//  none
//
real convert(real value, int old_units, int new_units)
{
  real temp;
  real result;

  // Convert current value to 10ths of microns.
  switch(old_units) {
    case U_MICRONS:
    temp = value * INTERNALS_PER_MICRON;
    break;
    case U_MILLIMETERS:
    temp = value * INTERNALS_PER_MM;
    break;
    case U_MILS:
    temp = value * INTERNALS_PER_MIL;
    break;
    case U_INCHES:
    temp = value * INTERNALS_PER_INCH;
    break;
    case U_INTERNALS:
    temp = value;
    break;
    default:
    dlgMessageBox(":Invalid value for old_units in convert()");
    exit(0);
  }

  // Convert temp value to the new unit of measure.
  switch(new_units) {
    case U_MICRONS:
    result = temp / INTERNALS_PER_MICRON;
    break;
    case U_MILLIMETERS:
    result = temp / INTERNALS_PER_MM;
    break;
    case U_MILS:
    result = temp / INTERNALS_PER_MIL;
    break;
    case U_INCHES:
    result = temp / INTERNALS_PER_INCH;
    break;
    case U_INTERNALS:
    result = temp;
    break;
    default:
    dlgMessageBox(":Invalid value for new_units (" + int_to_string(new_units) + ") in convert()");
    exit(0);
  }

  return result;
}

//
// 
// Convert a string with a number and a unit-of-measure suffix into 
// Eagle internal units (microns).
//
//    0.032in   0.032 inches
//    62ml      62 mils, 0.062 inches
//    0.43mm    0.43 millimeters
//    1500mc    1500 microns, 1.500 millimeters
//    60#       60 wire gage drill (0.040" or 1.016mm)
//    0.12      0.12 inches
//    0.60      0.60 millimeters
//    43        43 wire gage drill
//
// Params:
//  s   String with number followed by suffix.
// Returns:
//  int   Value in internal units.
// Changes:
//  none
//
int conv_to_units(string s)
{
  int val;
  string units;
  real num;
  real mm;
  string str;
  
  s = strlwr(s);
  
  units = get_units(s);
  num = strtod(s);
  
  if(units == "mm")
    val = convert(num, U_MILLIMETERS, U_INTERNALS);
  else if (units == "in")
    val = convert(num, U_INCHES, U_INTERNALS);
  else if (units == "ml")
    val = convert(num, U_MILS, U_INTERNALS);
  else if (units == "mc")
    val = num;
  else if (units == "#") {
    val = convert(get_drill_size_inches(num), U_INCHES, U_INTERNALS);
  }
  else {
    if (num <= 0.25)
      val = convert(num, U_INCHES, U_INTERNALS);
    else if (num <= 6) 
      val = convert(num, U_MILLIMETERS, U_INTERNALS);
    else
      val = convert(get_drill_size_inches(num), U_INCHES, U_INTERNALS);
  }

  return val;
}

//
// Determine if an integer value is within a range (inclusive).
//
// Params:
//  val       Value to test.
//  min_val   Low range value.
//  max_val   High range value.
// Returns:
//  int True if the value lies between min_val and max_val, inclusive.
// Changes:
//  none
//
int in_range_int(int val, int min_val, int max_val)
{
  return((val >= min_val) && (val <= max_val));
}

//
// Determine if two values should be considered "close."
//
// Params:
//  val_a   First value to compare.
//  val_b   Second value to compare.
// Returns:
//  int   True if values are close.
// Changes:
//  none
//
int close(real val_a, real val_b)
{
	return(abs(val_a - val_b) < EPSILON);
}

//
// Determines if a pair of points are "close" to one another.
//
// Params:
//  xa, xb  X values to compare.
//  ya, yb  Y values to compare.
// Returns:
//  int   True if points are close.
// Changes:
//  none
//
int close2(real xa, real xb, real ya, real yb)
{
	if (close(xa, xb) && close(ya, yb)) {
		return true;
	}
	return false;
}

//
// Determine if a pair of 3D points are "close" to one another.
//
// Params:
//  xa, xb  X values to compare.
//  ya, yb  Y values to compare.
//  za, zb  Z values to compare.
// Returns:
//  int   True if points are close.
// Changes:
//  none
//
int close3(real xa, real xb, real ya, real yb, real za, real zb)
{
	if (close(xa, xb) && close(ya, yb) && close(za, zb)) {
		return true;
	}
	return false;
}
