/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 */
package org.gatein.pc.test.url;

import junit.framework.TestCase;
import org.gatein.common.util.ParameterMap;

import java.util.ArrayList;
import java.util.Arrays;

public class DecoderTestCase extends TestCase
{

   Tester tester;

   public void setUp() throws Exception
   {
      tester = new Tester();
   }

   public void testNoMeta()
   {
      tester.assertActual(0);
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m1", "a");
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();
   }

   public void testNoMetaCorruption()
   {
      tester.parameter("action", "a").assertFailed().checkAndReset();
      tester.parameter("action", "1").assertFailed().checkAndReset();
   }

   public void testOneMeta()
   {
      tester.builder.addMetaParameter("m1");

      //
      tester.assertActual(0);
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m1", "a");
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", "foo");
      tester.assertActual(0);
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", new String[]{"foo","a"});
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("m2", "b");
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m1", "a").parameter("m2", "b");
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", "foo").parameter("m2", "b");
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", new String[]{"foo","a"}).parameter("m2", "b");
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();
   }

   public void testOneMetaCorruption()
   {
      tester.builder.addMetaParameter("m1");

      //
      tester.parameter("action", "a").assertFailed().checkAndReset();
      tester.parameter("action", "1").assertFailed().checkAndReset();
      tester.parameter("action", "10").assertFailed().checkAndReset();

      //
      tester.parameter("action", "a").assertFailed().checkAndReset();
      tester.parameter("action", "10").parameter("m1", "foo").assertFailed().checkAndReset();
   }

   public void testTwoMeta()
   {
      tester.builder.addMetaParameter("m1");
      tester.builder.addMetaParameter("m2");

      //
      tester.assertActual(0);
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m1", "a");
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m2", "b");
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      //
      tester.parameter("m1", "a").parameter("m2", "b");
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(0);
//      tester.check();
      tester.parameter("action", "0");
      tester.checkAndReset();

      // {m1}

      //
      tester.parameter("action", "10").parameter("m1", "foo");
      tester.assertActual(0);
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("action", "10").parameter("m1", new String[]{"foo","a"});
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("action", "10").parameter("m1", "foo").parameter("m2", "b");
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      //
      tester.parameter("action", "10").parameter("m1", new String[]{"foo","a"}).parameter("m2", "b");
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m1", new String[]{"foo"});
      tester.checkAndReset();

      // {m2}

      //
      tester.parameter("action", "1").parameter("m2", "bar");
      tester.assertActual(0);
      tester.assertMeta(1).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", "a").parameter("m2", "bar");
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(1).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m2", new String[]{"bar","b"});
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "1").parameter("m1", "a").parameter("m2", new String[]{"bar","b"});
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(1).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      // {m1,m2}

      //
      tester.parameter("action", "11").parameter("m1", "foo").parameter("m2", "bar");
      tester.assertActual(0);
      tester.assertMeta(2).assertMeta("m1", new String[]{"foo"}).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "11").parameter("m1", new String[]{"foo","a"}).parameter("m2", "bar");
      tester.assertActual(1).assertActual("m1", new String[]{"a"});
      tester.assertMeta(2).assertMeta("m1", new String[]{"foo"}).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "11").parameter("m1", "foo").parameter("m2", new String[]{"bar","b"});
      tester.assertActual(1).assertActual("m2", new String[]{"b"});
      tester.assertMeta(2).assertMeta("m1", new String[]{"foo"}).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();

      //
      tester.parameter("action", "11").parameter("m1", new String[]{"foo","a"}).parameter("m2", new String[]{"bar","b"});
      tester.assertActual(2).assertActual("m1", new String[]{"a"}).assertActual("m2", new String[]{"b"});
      tester.assertMeta(2).assertMeta("m1", new String[]{"foo"}).assertMeta("m2", new String[]{"bar"});
      tester.checkAndReset();
   }

   private static class Tester
   {

      private interface Assertion
      {
         void doAssert();
      }

      ArrayList assertions = new ArrayList();

      CodecBuilder builder = new CodecBuilder("action");
      ParameterMap parameters = new ParameterMap();
      ParameterMap actualParameters = new ParameterMap();
      ParameterMap metaParameters = new ParameterMap();
      boolean failed;

      public void check()
      {
         ParameterDecoder decoder = builder.createDecoder();

         //
         try
         {
            decoder.decode(parameters);

            //
            failed = false;
            actualParameters = decoder.getActualParameters();
            metaParameters = decoder.getMetaParameters();
         }
         catch (IllegalArgumentException e)
         {
            failed = true;
         }


         for (int i = 0; i < assertions.size(); i++)
         {
            Assertion assertion = (Assertion)assertions.get(i);
            assertion.doAssert();
         }
      }

      public void checkAndReset()
      {
         check();

         //
         parameters.clear();
         assertions.clear();
      }

      public Tester parameter(String name, String value)
      {
         if (value == null)
         {
            parameters.remove(name);
         }
         else
         {
            parameters.setValue(name, value);
         }
         return this;
      }
      public Tester parameter(String name, String[] values)
      {
         parameters.setValues(name, values);
         return this;
      }


      public Tester assertActual(final int size)
      {
         assertions.add(new Assertion()
         {
            public void doAssert()
            {
               assertFalse(failed);
               assertEquals(size, actualParameters.size());
            }
         });
         return this;
      }
      public Tester assertActual(final String name, final String[] values)
      {
         assertions.add(new Assertion()
         {
            public void doAssert()
            {
               assertFalse(failed);
               assertEquals(Arrays.asList(values), Arrays.asList(actualParameters.getValues(name)));
            }
         });
         return this;
      }
      public Tester assertMeta(final int size)
      {
         assertions.add(new Assertion()
         {
            public void doAssert()
            {
               assertFalse(failed);
               assertEquals(size, metaParameters.size());
            }
         });
         return this;
      }
      public Tester assertMeta(final String name, final String[] values)
      {
         assertions.add(new Assertion()
         {
            public void doAssert()
            {
               assertFalse(failed);
               assertEquals(Arrays.asList(values), Arrays.asList(metaParameters.getValues(name)));
            }
         });
         return this;
      }
      public Tester assertFailed()
      {
         assertions.add(new Assertion()
         {
            public void doAssert()
            {
               assertTrue(failed);
            }
         });
         return this;
      }
   }
}
