//////////////////////////////////////////////////////////////////////////////
// 
//                    Copyright 2019, Cornutum Project
//                             www.cornutum.org
//
//////////////////////////////////////////////////////////////////////////////

package org.cornutum.tcases.openapi;

import java.text.SimpleDateFormat;
import java.util.List;
import java.util.UUID;
import static java.util.stream.Collectors.toList;

/**
 * Defines a formatted string for a specific object.
 */
public abstract class FormattedString
  {
  /**
   * Creates a FormattedString for the given object.
   */
  public static FormattedString of( String format, Object object)
    {
    return
      object == null?
      new Null() :
      
      "binary".equals( format)?
      new Base64( (byte[]) object) :
      
      "byte".equals( format)?
      new Base64( (byte[]) object) :
      
      "date".equals( format)?
      new Date( (java.util.Date) object) :
      
      "date-time".equals( format)?
      new DateTime( (java.util.Date) object) :
      
      "uuid".equals( format)?
      new Uuid( (UUID) object) :
      
      new Native( object);
    }

  /**
   * Creates a FormattedString for each of the given objects.
   */
  public static List<FormattedString> of( String format, List<?> objects)
    {
    return objects.stream().map( object -> of( format, object)).collect( toList());
    }

  public String toString()
    {
    return formatted();
    }

  /**
   * Returns the formatted string representation of this object.
   */
  public abstract String formatted();

  /**
   * Uses the null string representation of this object.
   */
  public static class Null extends FormattedString
    {
    /**
     * Creates a new Null instance.
     */
    private Null()
      {
      }

    public String formatted()
      {
      return null;
      }
    }

  /**
   * Uses the native string representation of this object.
   */
  public static class Native extends FormattedString
    {
    /**
     * Creates a new Native instance.
     */
    private Native( Object object)
      {
      object_ = object;
      }

    public String formatted()
      {
      return object_.toString();
      }

    private Object object_;
    }

  /**
   * Uses the base64 string representation of this object.
   */
  public static class Base64 extends FormattedString
    {
    /**
     * Creates a new Base64 instance.
     */
    private Base64( byte[] object)
      {
      object_ = object;
      }

    public String formatted()
      {
      return java.util.Base64.getEncoder().encodeToString( object_);
      }

    private byte[] object_;
    }

  /**
   * Uses the RFC3339 full-date representation of this object.
   */
  public static class Date extends FormattedString
    {
    /**
     * Creates a new Date instance.
     */
    private Date( java.util.Date object)
      {
      object_ = object;
      }

    public String formatted()
      {
      return new SimpleDateFormat("yyyy-MM-dd").format( object_);
      }

    private java.util.Date object_;
    }

  /**
   * Uses the RFC3339 date-time representation of this object.
   */
  public static class DateTime extends FormattedString
    {
    /**
     * Creates a new DateTime instance.
     */
    private DateTime( java.util.Date object)
      {
      object_ = object;
      }

    public String formatted()
      {
      return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format( object_);
      }

    private java.util.Date object_;
    }

  /**
   * Uses the UUID string representation of this object.
   */
  public static class Uuid extends FormattedString
    {
    /**
     * Creates a new Uuid instance.
     */
    private Uuid( UUID object)
      {
      object_ = object;
      }

    public String formatted()
      {
      return object_.toString();
      }

    private UUID object_;
    }

  }

@@ -375,20 +375,94 @@
 va.util.
-UUID
+Optional;%0Aimport java.util.UUID;%0Aimport static java.util.Collections.emptyList
 ;%0Aimport
@@ -850,33 +850,24 @@
  new Base64(
- (byte%5B%5D)
  object) :%0A 
@@ -923,17 +923,8 @@
 e64(
- (byte%5B%5D)
  obj
@@ -982,33 +982,16 @@
 ew Date(
- (java.util.Date)
  object)
@@ -1058,25 +1058,8 @@
 ime(
- (java.util.Date)
  obj
@@ -1121,15 +1121,8 @@
 uid(
- (UUID)
  obj
@@ -1333,24 +1333,80 @@
   return
- objects
+%0A      Optional.ofNullable( objects).orElse( emptyList())%0A      
 .stream(
@@ -1406,16 +1406,23 @@
 stream()
+%0A      
 .map( ob
@@ -1449,16 +1449,23 @@
 object))
+%0A      
 .collect
@@ -2533,54 +2533,239 @@
 64( 
+Object object)%0A      %7B%0A      try%0A        %7B%0A        object_ = (
 byte%5B%5D
+)
  object
-)%0A      %7B%0A      object_ = object;
+;%0A        %7D%0A      catch( Exception e)%0A        %7B%0A        throw new IllegalStateException( String.format( %22Value=%25s is not a byte array%22, object));%0A        %7D
 %0A   
@@ -3123,16 +3123,78 @@
 e Date( 
+Object object)%0A      %7B%0A      try%0A        %7B%0A        object_ = (
 java.uti
@@ -3203,48 +3203,165 @@
 Date
+)
  object
-)%0A      %7B%0A      object_ = object;
+;%0A        %7D%0A      catch( Exception e)%0A        %7B%0A        throw new IllegalStateException( String.format( %22Value=%25s is not a Date%22, object));%0A        %7D
 %0A   
@@ -3736,16 +3736,78 @@
 teTime( 
+Object object)%0A      %7B%0A      try%0A        %7B%0A        object_ = (
 java.uti
@@ -3816,48 +3816,165 @@
 Date
+)
  object
-)%0A      %7B%0A      object_ = object;
+;%0A        %7D%0A      catch( Exception e)%0A        %7B%0A        throw new IllegalStateException( String.format( %22Value=%25s is not a Date%22, object));%0A        %7D
 %0A   
@@ -4353,52 +4353,231 @@
 id( 
+Object object)%0A      %7B%0A      try%0A        %7B%0A        object_ = (
 UUID
+)
  object
-)%0A      %7B%0A      object_ = object;
+;%0A        %7D%0A      catch( Exception e)%0A        %7B%0A        throw new IllegalStateException( String.format( %22Value=%25s is not a UUID%22, object));%0A        %7D
 %0A   

Improve error reported when value type is inconsistent with format