/* Copyright 2025 FSFLA
   Alexandre Oliva <lxoliva@fsfla.org>

   Este arquivo é parte do IRPF-Livre.

   IRPF-Livre é Software Livre; você pode redistribuir e/ou modificar
   a maior parte dele, inclusive este arquivo, de acordo com os termos
   da GNU Lesser General Public License publicada pela Free Software
   Foundation; tanto a versão 2.1 dessa licença, quanto (à sua
   escolha) qualquer versão posterior.

   Alguns componentes podem ser distribuídos e/ou modificados de
   acordo com outras licenças, incluídas no executável e consolidadas
   no arquivo list/licenses nos fontes.

   IRPF-Livre é distribuído na esperança de que ele seja útil, mas SEM
   QUALQUER GARANTIA; até mesmo sem qualquer garantia implícita de
   COMERCIABILIDADE OU ADEQUAÇÃO A UMA FINALIDADE ESPECÍFICA. Consulte
   a GNU Lesser General Public License para mais detalhes.

   Você deve ter recebido uma cópia da GNU Lesser General Public
   License junto com IRPF-Livre; se não, escreva para a Free Software
   Foundation, Inc., no endereço 59 Temple Street, Suite 330, Boston,
   MA 02111-1307 USA.

   Veja uma tradução não oficial da licença em
   http://creativecommons.org/licenses/LGPL/2.1/legalcode.pt


   This file is part of IRPF-Livre.

   IRPF-Livre is free software; you can redistribute and/or modify
   most of it, this file included, under the terms of the GNU Lesser
   General Public License as published by the Free Software
   Foundation; either version 2.1 of the License, or (at your option)
   any later version.

   Some components can be distributed and/or modified under different
   licenses, included in the executable and consolidated in the file
   list/licenses in the source tree.

   IRPF-Livre 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with IRPF-Livre; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
*/

package serpro.ppgd.irpf.gcap;
import java.util.Iterator;
import serpro.ppgd.irpf.gcap.alienacao.AlienacaoBensImoveis;
import serpro.ppgd.irpf.gcap.alienacao.AlienacaoBemImovel;
import serpro.ppgd.irpf.gcap.alienacao.AlienacaoBensMoveis;
import serpro.ppgd.irpf.gcap.alienacao.AlienacaoItem;
import serpro.ppgd.irpf.gcap.alienacao.AlienacaoParticipacoesSocietarias;
import serpro.ppgd.irpf.gcap.apuracao.ApuracaoBemImovel;
import serpro.ppgd.irpf.gcap.consolidacao.ColecaoConsolidacao;
import serpro.ppgd.irpf.gcap.consolidacao.Consolidacao;
import serpro.ppgd.negocio.CPF;
import serpro.ppgd.negocio.ObjetoNegocio;
import serpro.ppgd.negocio.Valor;

public class GanhosDeCapital extends ObjetoNegocio
{
  private ColecaoIdDemonstrativoGCAP ids = new ColecaoIdDemonstrativoGCAP ();
  private AlienacaoBensImoveis bensImoveis = new AlienacaoBensImoveis ();
  private AlienacaoBensMoveis bensMoveis = new AlienacaoBensMoveis ();
  private AlienacaoParticipacoesSocietarias pSocietarias = new AlienacaoParticipacoesSocietarias ();
  /* FIXME: especie */
  private ColecaoConsolidacao consolidacoesBrasil = new ColecaoConsolidacao ();
  private ColecaoConsolidacao consolidacoesExterior = new ColecaoConsolidacao ();
  private Consolidacao consolidacaoGeralBrasil = new Consolidacao ();
  private Consolidacao consolidacaoGeralExterior = new Consolidacao ();
  /* FIXME: consolidacaoGeralEspecie, colecaoTotalizacaoMoedasAlienadas */

  private Valor totalImpostoPagoSobreGanhosCapital = new Valor (this, "");

  public ColecaoIdDemonstrativoGCAP getIds ()
  {
    return ids;
  }

  public AlienacaoBensImoveis getBensImoveis ()
  {
    return bensImoveis;
  }

  public AlienacaoBensMoveis getBensMoveis ()
  {
    return bensMoveis;
  }

  public AlienacaoParticipacoesSocietarias getPSocietarias ()
  {
    return pSocietarias;
  }

  public ColecaoConsolidacao getConsolidacoesBrasil ()
  {
    return consolidacoesBrasil;
  }

  public ColecaoConsolidacao getConsolidacoesExterior ()
  {
    return consolidacoesExterior;
  }

  public Consolidacao getConsolidacaoGeralBrasil ()
  {
    return consolidacaoGeralBrasil;
  }

  public Consolidacao getConsolidacaoGeralExterior ()
  {
    return consolidacaoGeralExterior;
  }

  public Valor getTotalImpostoPagoSobreGanhosCapital ()
  {
    return totalImpostoPagoSobreGanhosCapital;
  }

  public static final int EvitaBrasil = 1;
  public static final int EvitaExterior = 2;
  private static final int brex = 0;
  private static final int bxmask = EvitaBrasil | EvitaExterior;
  private static final int sobr = bxmask ^ EvitaBrasil;
  private static final int soex = bxmask ^ EvitaExterior;
  public static final int EvitaConsolidacaoGeral = 4;
  public static final int EvitaImoveis = 8;
  public static final int EvitaMoveis = 16;
  public static final int EvitaPartSoc = 32;
  public static final int EvitaConsolidacoesCPF = 64;
  private static final int qmask = EvitaImoveis | EvitaMoveis | EvitaPartSoc;
  private static final int soIm = qmask ^ EvitaImoveis;
  private static final int soMv = qmask ^ EvitaMoveis;
  private static final int soPS = qmask ^ EvitaPartSoc;
  private static final int evcg = (EvitaConsolidacaoGeral
				  | EvitaImoveis | EvitaMoveis | EvitaPartSoc);
  private static final int evc = (evcg | EvitaConsolidacoesCPF);
  private static final int evcq = (EvitaConsolidacaoGeral | EvitaConsolidacoesCPF);
  private abstract class obtemValor {
    /* Pelo menos um dos seguintes deve ser especializado.  */
    public Valor getValor (Consolidacao c)
    { return null; }
    public Valor getValor (AlienacaoItem i, int que)
    { return ((que & bxmask) != brex) ? null : getValor (i.getConsolidacao ()); }
  }
  private Valor totalizaConsolidado (CPF cpf, obtemValor o,
				     Valor /* in out */ total, Iterator it)
  {
    while (it.hasNext ())
      {
	Consolidacao c = (Consolidacao) it.next ();
	if (cpf != null && !cpf.asString ().equals (c.getCpf ().asString ()))
	  continue;
	total.append ('+', o.getValor (c));
      }
    return total;
  }
  private Valor totalizaItens (CPF cpf, obtemValor o, int que,
			       Valor /* in out */ total, Iterator it)
  {
    while (it.hasNext ())
      {
	AlienacaoItem c = (AlienacaoItem) it.next ();
	if (cpf != null && !cpf.asString ().equals (c.getCpf ().asString ()))
	  continue;
	total.append ('+', o.getValor (c, que));
      }
    return total;
  }
  private Valor totaliza (CPF cpf, int que, obtemValor o)
  {
    Valor total = new Valor ();
    if ((que & bxmask) == bxmask)
      /* Evitando tanto Brasil quanto Exterior, Nada há a calcular.  */;
    else if (cpf == null && (que & evcg) == 0)
      {
	if ((que & EvitaBrasil) == 0)
	  total.append ('+', o.getValor (getConsolidacaoGeralBrasil ()));
	if ((que & EvitaExterior) == 0)
	  total.append ('+', o.getValor (getConsolidacaoGeralExterior ()));
      }
    else if ((que & evc) == 0)
      {
	if ((que & EvitaBrasil) == 0)
	  totalizaConsolidado (cpf, o, total,
			       getConsolidacoesBrasil ().recuperarLista ().iterator ());

	if ((que & EvitaExterior) == 0)
	  totalizaConsolidado (cpf, o, total,
			       getConsolidacoesExterior ().recuperarLista ().iterator ());
      }
    else
      {
	if ((que & EvitaImoveis) == 0)
	  totalizaItens (cpf, o, (que & bxmask) | soIm, total,
			 getBensImoveis ().recuperarLista ().iterator ());
	if ((que & EvitaMoveis) == 0)
	  totalizaItens (cpf, o, (que & bxmask) | soMv, total,
			 getBensMoveis ().recuperarLista ().iterator ());
	if ((que & EvitaPartSoc) == 0)
	  totalizaItens (cpf, o, (que & bxmask) | soPS, total,
			 getPSocietarias ().recuperarLista ().iterator ());
      }
    return total;
  }

  public Valor calculaIsento (CPF cpf, int que)
  {
    return totaliza (cpf, que,
		     new obtemValor () {
		       public Valor getValor (Consolidacao c)
		       { return c.getTotalRendIsentosNaoTributaveis (); }
		     });
  }

  public Valor calculaPequenoValor (CPF cpf, int que)
  {
    return totaliza (cpf, que | soIm | evcq,
		     new obtemValor () {
		       public Valor getValor (AlienacaoItem i, int que) {
			 ApuracaoBemImovel abi = ((AlienacaoBemImovel)i).getApuracao ();
			 switch (que & bxmask) {
			 case brex: return abi.getValorReducaoBemPequenoValor ();
			 case sobr: return abi.getValorReducaoBemPequenoValorOrigemMN ();
			 case soex: return abi.getValorReducaoBemPequenoValorOrigemME ();
			 default: return null;
			 }
		       }
		     });
  }

  public Valor calculaUnicoImovel (CPF cpf, int que)
  {
    return totaliza (cpf, que | soIm | evcq,
		     new obtemValor () {
		       public Valor getValor (AlienacaoItem i, int que) {
			 ApuracaoBemImovel abi = ((AlienacaoBemImovel)i).getApuracao ();
			 switch (que & bxmask) {
			 case brex: return abi.getValorReducaoUnicoImovel ();
			 case sobr: return abi.getValorReducaoUnicoImovelOrigemMN ();
			 case soex: return abi.getValorReducaoUnicoImovelOrigemME ();
			 default: return null;
			 }
		       }
		     });
  }

  public Valor calculaReducao (CPF cpf, int que)
  {
    Valor v = new Valor ();
    v.append ('+', calculaIsento (cpf, que));
    v.append ('-', calculaPequenoValor (cpf, que));
    v.append ('-', calculaUnicoImovel (cpf, que));
    return v;
  }

  public Valor calculaTribExcl (CPF cpf, int que)
  {
    return totaliza (cpf, que,
		     new obtemValor () {
		       public Valor getValor (Consolidacao c)
		       { return c.getTotalRendSujeitosTributacao (); }
		     });
  }

  public Valor calculaImpostoDevido (CPF cpf, int que)
  {
    return totaliza (cpf, que,
		     new obtemValor () {
		       public Valor getValor (Consolidacao c)
		       { return c.getImpostoDevidoAnoAtual (); }
		     });
  }

  public Valor calculaImpostoPago (CPF cpf, int que)
  {
    return totaliza (cpf, que,
		     new obtemValor () {
		       public Valor getValor (Consolidacao c)
		       { return c.getTotalImpostoPago (); }
		     });
  }

  public Valor calculaImpostoDiferidoPosterior (CPF cpf, int que)
  {
    return totaliza (cpf, que,
		     new obtemValor () {
		       public Valor getValor (Consolidacao c)
		       { return c.getImpostoDiferidoAnosPosteriores (); }
		     });
  }

  public Valor calculaTotalImpostoPago ()
  {
    Valor v = new Valor ();
    v.append ('+', getConsolidacaoGeralBrasil ().getTotalImpostoPago ());
    v.append ('+', getConsolidacaoGeralExterior ().getTotalImpostoPago ());
    return v;
  }

}
