/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  (this file is based on PCB, interactive printed circuit board design)
 *  Copyright (C) 2011 Andrew Poelstra
 *  Copyright (C) 2016,2019 Tibor 'Igor2' Palinkas
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 *
 *
 *  Old contact info:
 *  Andrew Poelstra, 16966 60A Ave, V3S 8X5 Surrey, BC, Canada
 *  asp11@sfu.ca
 *
 */

#ifndef PCB_UNIT_H
#define PCB_UNIT_H

#include "global_typedefs.h"

enum pcb_allow_e {
	PCB_UNIT_NO_PRINT = 0, /* suffixes we can read but not print (i.e., "inch") */
	PCB_UNIT_ALLOW_NM = 1,
	PCB_UNIT_ALLOW_UM = 2,
	PCB_UNIT_ALLOW_MM = 4,
	PCB_UNIT_ALLOW_CM = 8,
	PCB_UNIT_ALLOW_M = 16,
	PCB_UNIT_ALLOW_KM = 32,
	PCB_UNIT_ALLOW_DU = 64, /* "du" = 0.1 micron "decimicron" units, for eagle bin */

	PCB_UNIT_ALLOW_CMIL = 1024,
	PCB_UNIT_ALLOW_MIL = 2048,
	PCB_UNIT_ALLOW_IN = 4096,

	PCB_UNIT_ALLOW_DMIL = 8192, /* for kicad legacy decimil units */

	PCB_UNIT_ALLOW_HZ = 16384,
	PCB_UNIT_ALLOW_KHZ = 32768,
	PCB_UNIT_ALLOW_MHZ = 65536,
	PCB_UNIT_ALLOW_GHZ = 131072,

	PCB_UNIT_ALLOW_METRIC = PCB_UNIT_ALLOW_NM  | PCB_UNIT_ALLOW_UM | PCB_UNIT_ALLOW_MM | PCB_UNIT_ALLOW_CM | PCB_UNIT_ALLOW_M | PCB_UNIT_ALLOW_KM,
	PCB_UNIT_ALLOW_IMPERIAL = PCB_UNIT_ALLOW_DMIL | PCB_UNIT_ALLOW_CMIL | PCB_UNIT_ALLOW_MIL | PCB_UNIT_ALLOW_IN,
	PCB_UNIT_ALLOW_FREQ = PCB_UNIT_ALLOW_HZ | PCB_UNIT_ALLOW_KHZ | PCB_UNIT_ALLOW_MHZ | PCB_UNIT_ALLOW_GHZ,

	/* DO NOT USE - this is all units allowed in %mr and io_pcb old format */
	PCB_UNIT_ALLOW_READABLE = PCB_UNIT_ALLOW_CMIL,

	/* Used for pcb-printf %mS - should not include unusual units like km, cmil and dmil */
	PCB_UNIT_ALLOW_NATURAL = PCB_UNIT_ALLOW_NM | PCB_UNIT_ALLOW_UM | PCB_UNIT_ALLOW_MM | PCB_UNIT_ALLOW_M | PCB_UNIT_ALLOW_MIL | PCB_UNIT_ALLOW_IN,

	/* Allow all but the most exotic */
	PCB_UNIT_ALLOW_ALL_SANE = ~(PCB_UNIT_ALLOW_DU | PCB_UNIT_ALLOW_DMIL),

	PCB_UNIT_ALLOW_ALL = ~0
};

/* bitfield */
typedef enum pcb_family_e {
	PCB_UNIT_METRIC   = 1,
	PCB_UNIT_IMPERIAL = 2,
	PCB_UNIT_FREQ     = 4
} pcb_family_t;

enum pcb_suffix_e { PCB_UNIT_NO_SUFFIX, PCB_UNIT_SUFFIX, PCB_UNIT_FILE_MODE };

struct pcb_unit_s {
	const char *suffix;
	char printf_code;        /* 0 means the unit is an alias and should not be used in printf */
	double scale_factor;
	enum pcb_family_e family;
	enum pcb_allow_e allow;
	int default_prec;

	/* autogenerated */
	int index; /* Index into Unit[] list */
};

extern pcb_unit_t pcb_units[];

/* Look up a given suffix in the units array. Pluralized units are supported.
   The _ version allows strict (full name match) lookup if strict iz non-zero. */
const pcb_unit_t *get_unit_struct(const char *suffix);
const pcb_unit_t *get_unit_struct_(const char *suffix, int strict);

/* Return the furst unit that matches allow */
const pcb_unit_t *get_unit_struct_by_allow(enum pcb_allow_e allow);

/* Return the list of units and number of units (use only when listing units);
   Normally returns basic units only (aliases_too==0); if aliases_too is
   non-zero, also include redundant (less favorable) units */
int pcb_get_n_units(int aliases_too);

/* Return the idxth unit or NULL (bounds check) */
const pcb_unit_t *get_unit_by_idx(int idx);

/* Return the unit with (case sensitive) matching suffix */
const pcb_unit_t *get_unit_by_suffix(const char *suffix);

/* Convert x to the given unit */
double pcb_coord_to_unit(const pcb_unit_t *unit, pcb_coord_t x);

/* Return how many PCB-internal-Coord-unit a unit translates to */
double pcb_unit_to_factor(const pcb_unit_t * unit);

/* Convert a given unit to pcb coords; clamp at the end of the ranges */
pcb_coord_t pcb_unit_to_coord(const pcb_unit_t * unit, double x);

/* Bring an angle into [0, 360) range */
pcb_angle_t pcb_normalize_angle(pcb_angle_t a);


/* Initialize non-static unit data: assigns each unit its index for
   quick access through the main units array, and internationalize
   the units for GUI display. */
void pcb_units_init(void);

/* PCB/physical unit conversions */
#define PCB_COORD_TO_MIL(n)      ((n) / 25400.0)
#define PCB_MIL_TO_COORD(n)      ((n) * 25400.0)
#define PCB_COORD_TO_MM(n)       ((n) / 1000000.0)
#define PCB_MM_TO_COORD(n)       ((n) * 1000000.0)
#define PCB_COORD_TO_INCH(n)     (PCB_COORD_TO_MIL(n) / 1000.0)
#define PCB_INCH_TO_COORD(n)     (PCB_MIL_TO_COORD(n) * 1000.0)

#define PCB_SWAP_ANGLE(a)        (-(a))
#define PCB_SWAP_DELTA(d)        (-(d))

PCB_INLINE pcb_coord_t pcb_coord_abs(pcb_coord_t c)
{
	if (c < 0) return -c;
	return c;
}

#endif
