"""Проверки сохранённых таблиц без открытия модели и без решения поля."""
import math


def u_curve_diagnostic(evaluations, voltage=20000., power=150e6, power_scale=300e6):
    if not evaluations:raise ValueError('Нет завершённых полевых оценок')
    rows=[]
    for value in evaluations:
        residual=[(value['U_line_rms_V']-voltage)/voltage,
                  (value['P_generator_W']-power)/power_scale]
        rows.append({'evaluation':value['evaluation'],'currents_A':value['currents_A'],
                     'U_line_V':value['U_line_rms_V'],'P_W':value['P_generator_W'],
                     'I_rms_A':value['I_phase_rms_A'],'residual':residual,
                     'residual_max':max(abs(x) for x in residual)})
    return {'theoretical_I_min_A':power/(math.sqrt(3)*voltage),
            'best_evaluation':min(rows,key=lambda r:r['residual_max']),
            'last_evaluation':rows[-1],'completed_evaluations':len(rows),
            'accepted_curve_points':0,'curve_verified':False,
            'note':'Оценки прерванного поиска не являются точками характеристики.'}


def net_power(row, rs, rf, omega, voltage=None, resistance=None):
    d,q,f=row['currents_A'];ld,lq,lf=row['flux_Wb']
    uf=row['field_voltage_V'] if voltage is None else voltage
    load=row['load_ohm'] if resistance is None else resistance
    return uf*f-rf*f*f-1.5*rs*(d*d+q*q)-1.5*(load or 0.)*(d*d+q*q)-omega*1.5*(ld*q-lq*d)


def trajectory_energy(rows,rs,rf,omega,absolute_scale=1000.):
    """Работа заново из i, λ и внешних входов, без input_work_J/torque_Nm."""
    if len(rows)<2:raise ValueError('Нужна траектория')
    work=0.;worst=0.;max_stored_difference=0.;event_intervals=0
    for k in range(1,len(rows)):
        a,b=rows[k-1],rows[k];dt=b['time_s']-a['time_s']
        if dt<=0:raise ValueError('Время должно строго возрастать')
        # В конце интервала, заканчивающегося переключением, берётся левый вход.
        old_load=a['load_ohm']
        before=dict(b,field_voltage_V=a['field_voltage_V'],load_ohm=old_load)
        work+=dt*(net_power(a,rs,rf,omega)+net_power(before,rs,rf,omega))/2
        error=b['energy_J']-rows[0]['energy_J']-work
        worst=max(worst,abs(error))
        max_stored_difference=max(max_stored_difference,abs(work-b['input_work_J']))
        if a['load_ohm']!=b['load_ohm'] or a['field_voltage_V']!=b['field_voltage_V']:event_intervals+=1
    scale=max(absolute_scale,max(abs(r['energy_J']-rows[0]['energy_J']) for r in rows))
    return {'scaled_energy_residual':worst/scale,'max_work_difference_J':max_stored_difference,
            'scale_J':scale,'event_intervals':event_intervals,'passed':worst/scale<=.01}


def time_step_error(coarse,fine):
    if coarse[0]['time_s']!=fine[0]['time_s'] or abs(coarse[-1]['time_s']-fine[-1]['time_s'])>1e-9:
        raise ValueError('Разные интервалы сравнения')
    scales=[max(1.,max(abs(r['currents_A'][j]) for r in fine)) for j in range(3)]
    errors=[0.,0.,0.];index=0
    for row in coarse:
        t=row['time_s']
        while index<len(fine)-2 and fine[index+1]['time_s']<t:index+=1
        a,b=fine[index],fine[index+1];fraction=(t-a['time_s'])/(b['time_s']-a['time_s'])
        for j in range(3):
            ref=a['currents_A'][j]+fraction*(b['currents_A'][j]-a['currents_A'][j])
            errors[j]=max(errors[j],abs(row['currents_A'][j]-ref)/scales[j])
    return {'scaled_current_errors':errors,'passed':max(errors)<=.01}


def control_requests(rows,event=.02):
    """План будущей независимой проверки, а не результаты FEMM."""
    selected={0:['initial'],len(rows)-1:['final']}
    for t in (event,event+1e-6,event+1e-4,event+1e-3,event+5e-3):
        k=min(range(len(rows)),key=lambda j:abs(rows[j]['time_s']-t))
        selected.setdefault(k,[]).append('nearest_requested_time_'+str(t))
    for component in range(3):
        for mode,fun in [('minimum',min),('maximum',max)]:
            k=fun(range(len(rows)),key=lambda j:rows[j]['currents_A'][component])
            selected.setdefault(k,[]).append(mode+'_current_'+str(component))
    return [{'row_index':k,'time_s':rows[k]['time_s'],'currents_A':rows[k]['currents_A'],
             'map_flux_Wb':rows[k]['flux_Wb'],'selection_reasons':selected[k],
             'independent_FEMM_flux_Wb':None,'passed':None} for k in sorted(selected)]
