/*
 * Copyright (c) 2013, 2020 Oracle and/or its affiliates and others.
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.ts.tests.websocket.common.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import jakarta.websocket.Extension;

/**
 * For comparable purposes, this implementation is either case sensitive, or
 * case insensitive. As the extension parameters appear in the http headers and
 * RFC 2616 says : Field names are case-insensitive, the default is case
 * insensitive.
 */
public class ExtensionImpl implements Extension, Comparable<Extension> {

  protected List<Parameter> list = new ArrayList<>();

  protected String name;

  protected boolean caseSensitive = false;

  public void caseSensitive(boolean caseSensitive) {
    this.caseSensitive = caseSensitive;
  }

  /**
   * An Extension Implementation. For empty parameter list, use null
   * 
   * @param name
   *          Extension name, see {@link Extension#getName()}
   * @param params
   *          Extension Parameter list, see {@link Extension#getParameters()}
   */
  public ExtensionImpl(String name, Parameter... params) {
    super();
    this.name = name;
    addParameters(params);
  }

  public ExtensionImpl(Extension extension) {
    this(extension.getName(),
        extension.getParameters().toArray(new Parameter[0]));
  }

  public void addParameters(Parameter... params) {
    if (params != null)
      for (Parameter param : params)
        list.add(param);
  }

  @Override
  public String getName() {
    return name;
  }

  @Override
  public List<Parameter> getParameters() {
    return list;
  }

  @Override
  public int compareTo(Extension o) {
    int cmp = caseSensitive ? name.compareTo(o.getName())
        : name.compareToIgnoreCase(o.getName());
    if (cmp != 0)
      return cmp;
    cmp = getParameters().size() - o.getParameters().size();
    if (cmp != 0)
      return (int) Math.signum(cmp);

    List<ExtensionParameterImpl> newThisList = getExtensionParameters(this);
    List<ExtensionParameterImpl> newOtherList = getExtensionParameters(o);
    Collections.sort(newThisList);
    Collections.sort(newOtherList);
    Iterator<ExtensionParameterImpl> i = newThisList.iterator();
    Iterator<ExtensionParameterImpl> j = newOtherList.iterator();
    while (i.hasNext() && cmp == 0) {
      ExtensionParameterImpl iItem = i.next();
      ExtensionParameterImpl jItem = j.next();
      cmp = iItem.compareTo(jItem);
    }
    return cmp;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Extension)
      return compareTo((Extension) obj) == 0;
    return false;
  }

  @Override
  public int hashCode() {
    return toString().hashCode();
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder().append(getClass().getSimpleName())
        .append("(name=\"").append(name).append("\", parameters=[");
    for (Parameter p : getExtensionParameters(this))
      sb.append(p.toString()).append(",");
    sb.append("])");
    return sb.toString();
  }

  /**
   * Can pass VI of Extension to have tck impl of Extension.Parameter list
   */
  public List<ExtensionParameterImpl> getExtensionParameters(
      Extension extension) {
    List<ExtensionParameterImpl> params = new ArrayList<>();
    for (Parameter item : extension.getParameters())
      params.add(new ExtensionParameterImpl(item, caseSensitive));
    Collections.sort(params);
    return params;
  }

  /**
   * Transform list of VI Extensions to List of TCK impl of Extension
   * 
   * @param extensions
   * @return
   */
  public static List<ExtensionImpl> transformToImpl(
      List<? extends Extension> extensions) {
    List<ExtensionImpl> list = new ArrayList<>();
    if (extensions != null)
      for (Extension ex : extensions)
        list.add(new ExtensionImpl(ex));
    // do not sort, the order of extensions is significant
    // (rfc6455#section-9.1)
    return list;
  }

  /**
   * For proper output, make sure the list contains ExtensionImpl items, see
   * {@link #transformToImpl(List)}, otherwise the output depends on overridden
   * {@linkplain #toString()} of given Extensions
   */
  public static String toString(List<? extends Extension> list) {
    StringBuilder sb = new StringBuilder().append("{");
    if (list != null)
      for (Extension ex : list)
        sb.append(ex.toString()).append(";");
    sb.append("}");
    return sb.toString();
  }
}
