# -*- coding: utf-8 -*-

"""
Dimensional analysis and unit systems.

This module defines dimension/unit systems and physical quantities. It is
based on a group-theoretical construction where dimensions are represented as
vectors (coefficients being the exponents), and units are defined as a dimension
to which we added a scale.

Quantities are built from a factor and a unit, and are the basic objects that
one will use when doing computations.

All objects except systems and prefixes can be used in sympy expressions.
Note that as part of a CAS, various objects do not combine automatically
under operations.

Details about the implementation can be found in the documentation, and we
will not repeat all the explanations we gave there concerning our approach.
Ideas about future developments can be found on the `Github wiki
<https://github.com/sympy/sympy/wiki/Unit-systems>`_, and you should consult
this page if you are willing to help.

Useful functions:

- ``find_unit``: easily lookup pre-defined units.
- ``convert_to(expr, newunit)``: converts an expression into the same
    expression expressed in another unit.

"""

from sympy.core.compatibility import string_types
from .dimensions import Dimension, DimensionSystem
from .unitsystem import UnitSystem
from .util import convert_to
from .quantities import Quantity

from .dimensions import (
    amount_of_substance, acceleration, action,
    capacitance, charge, conductance, current, energy,
    force, frequency, impedance, inductance, length,
    luminous_intensity, magnetic_density,
    magnetic_flux, mass, momentum, power, pressure, temperature, time,
    velocity, voltage, volume,
)

Unit = Quantity

speed = velocity
luminosity = luminous_intensity
magnetic_flux_density = magnetic_density
amount = amount_of_substance

from .prefixes import (
    # 10-power based:
    yotta,
    zetta,
    exa,
    peta,
    tera,
    giga,
    mega,
    kilo,
    hecto,
    deca,
    deci,
    centi,
    milli,
    micro,
    nano,
    pico,
    femto,
    atto,
    zepto,
    yocto,
    # 2-power based:
    kibi,
    mebi,
    gibi,
    tebi,
    pebi,
    exbi,
)

from .definitions import (
    percent, percents,
    permille,
    rad, radian, radians,
    deg, degree, degrees,
    sr, steradian, steradians,
    mil, angular_mil, angular_mils,
    m, meter, meters,
    kg, kilogram, kilograms,
    s, second, seconds,
    A, ampere, amperes,
    K, kelvin, kelvins,
    mol, mole, moles,
    cd, candela, candelas,
    g, gram, grams,
    mg, milligram, milligrams,
    ug, microgram, micrograms,
    newton, newtons, N,
    joule, joules, J,
    watt, watts, W,
    pascal, pascals, Pa, pa,
    hertz, hz, Hz,
    coulomb, coulombs, C,
    volt, volts, v, V,
    ohm, ohms,
    siemens, S, mho, mhos,
    farad, farads, F,
    henry, henrys, H,
    tesla, teslas, T,
    weber, webers, Wb, wb,
    optical_power, dioptre, D,
    lux, lx,
    km, kilometer, kilometers,
    dm, decimeter, decimeters,
    cm, centimeter, centimeters,
    mm, millimeter, millimeters,
    um, micrometer, micrometers, micron, microns,
    nm, nanometer, nanometers,
    pm, picometer, picometers,
    ft, foot, feet,
    inch, inches,
    yd, yard, yards,
    mi, mile, miles,
    nmi, nautical_mile, nautical_miles,
    l, liter, liters,
    dl, deciliter, deciliters,
    cl, centiliter, centiliters,
    ml, milliliter, milliliters,
    ms, millisecond, milliseconds,
    us, microsecond, microseconds,
    ns, nanosecond, nanoseconds,
    ps, picosecond, picoseconds,
    minute, minutes,
    h, hour, hours,
    day, days,
    anomalistic_year, anomalistic_years,
    sidereal_year, sidereal_years,
    tropical_year, tropical_years,
    common_year, common_years,
    julian_year, julian_years,
    draconic_year, draconic_years,
    gaussian_year, gaussian_years,
    full_moon_cycle, full_moon_cycles,
    year, years, tropical_year,
    G, gravitational_constant,
    c, speed_of_light,
    Z0,
    hbar,
    planck,
    eV, electronvolt, electronvolts,
    avogadro_number,
    avogadro, avogadro_constant,
    boltzmann, boltzmann_constant,
    R, molar_gas_constant,
    faraday_constant,
    josephson_constant,
    von_klitzing_constant,
    amu, amus, atomic_mass_unit, atomic_mass_constant,
    gee, gees, acceleration_due_to_gravity,
    u0, magnetic_constant,
    e0, electric_constant, vacuum_permittivity,
    Z0, vacuum_impedance,
    coulomb_constant, electric_force_constant,
    atmosphere, atmospheres, atm,
    kPa,
    bar, bars,
    pound, pounds,
    psi,
    dHg0,
    mmHg, torr,
    mmu, mmus, milli_mass_unit,
    quart, quarts,
    ly, lightyear, lightyears,
    au, astronomical_unit, astronomical_units,
    planck_mass,
    planck_time,
    planck_temperature,
    planck_length,
)


def find_unit(quantity):
    """
    Return a list of matching units or dimension names.

    - If ``quantity`` is a string -- units/dimensions containing the string
    `quantity`.
    - If ``quantity`` is a unit or dimension -- units having matching base
    units or dimensions.

    Examples
    ========

    >>> from sympy.physics import units as u
    >>> u.find_unit('charge')
    ['C', 'coulomb', 'coulombs']
    >>> u.find_unit(u.charge)
    ['C', 'coulomb', 'coulombs']
    >>> u.find_unit("ampere")
    ['ampere', 'amperes']
    >>> u.find_unit('volt')
    ['volt', 'volts', 'electronvolt', 'electronvolts']
    >>> u.find_unit(u.inch**3)[:5]
    ['l', 'cl', 'dl', 'ml', 'liter']
    """
    import sympy.physics.units as u
    rv = []
    if isinstance(quantity, string_types):
        rv = [i for i in dir(u) if quantity in i and isinstance(getattr(u, i), Quantity)]
        dim = getattr(u, quantity)
        if isinstance(dim, Dimension):
            rv.extend(find_unit(dim))
    else:
        for i in sorted(dir(u)):
            other = getattr(u, i)
            if not isinstance(other, Quantity):
                continue
            if isinstance(quantity, Quantity):
                if quantity.dimension == other.dimension:
                    rv.append(str(i))
            elif isinstance(quantity, Dimension):
                if other.dimension == quantity:
                    rv.append(str(i))
            elif other.dimension == Dimension(Quantity.get_dimensional_expr(quantity)):
                rv.append(str(i))

    return sorted(rv, key=len)

# NOTE: the old units module had additional variables:
# 'density', 'illuminance', 'resistance'.
# They were not dimensions, but units (old Unit class).
