/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.olingo.fit.tecsvc.http;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.BufferedOutputStream;
import java.io.InputStream;

import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;

import org.apache.commons.io.IOUtils;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.fit.AbstractBaseTestITCase;
import org.apache.olingo.fit.tecsvc.TecSvcConst;
import org.junit.Test;

public class BasicStreamITCase extends AbstractBaseTestITCase {

  private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/";

  @Test
  public void streamESStreamJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStream?$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("Streamed-Employee1@company.example\"," +
            "\"Streamed-Employee2@company.example\"," +
            "\"Streamed-Employee3@company.example\""));
    assertTrue(content.contains("\"PropertyString\":\"TEST 1->streamed\""));
    assertTrue(content.contains("\"PropertyString\":\"TEST 2->streamed\""));
  }
  
  @Test
  public void streamESWithStreamJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESWithStream?$expand=PropertyStream&$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));
  }

  @Test
  public void streamESStreamXml() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStream?$format=xml");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.APPLICATION_XML, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());
    assertTrue(content.contains("<m:element>Streamed-Employee1@company.example</m:element>" +
            "<m:element>Streamed-Employee2@company.example</m:element>" +
            "<m:element>Streamed-Employee3@company.example</m:element>"));
    assertTrue(content.contains("<d:PropertyString>TEST 1->streamed</d:PropertyString>"));
    assertTrue(content.contains("<d:PropertyString>TEST 2->streamed</d:PropertyString>"));
  }

  @Test
  public void streamESStreamServerSidePagingXml() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$format=xml");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.APPLICATION_XML, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());
    assertTrue(content.contains("<a:link rel=\"next\" href="));
    assertTrue(content.contains("ESStreamServerSidePaging?$format=xml&amp;%24skiptoken=1%2A10\"/>"));
    assertTrue(content.contains("<a:id>ESStreamServerSidePaging(1)</a:id>"));
    assertTrue(content.contains("<d:PropertyInt16 m:type=\"Int16\">1</d:PropertyInt16>"));
    assertTrue(content.contains("<d:PropertyStream m:type=\"Stream\">readLink</d:PropertyStream>"));
  }
  
  @Test
  public void streamESStreamServerSidePagingNextXml() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$format=xml&$skiptoken=1%2A10");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.APPLICATION_XML, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());
    assertTrue(content.contains("<a:link rel=\"next\" href="));
    assertTrue(content.contains("ESStreamServerSidePaging?$format=xml&amp;%24skiptoken=2%2A10\"/>"));
    assertTrue(content.contains("<a:id>ESStreamServerSidePaging(11)</a:id>"));
    assertTrue(content.contains("<d:PropertyInt16 m:type=\"Int16\">11</d:PropertyInt16>"));
    assertTrue(content.contains("<d:PropertyStream m:type=\"Stream\">readLink</d:PropertyStream>"));
  }
  
  @Test
  public void streamESStreamServerSidePagingJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("{\"PropertyInt16\":2,"+
    "\"PropertyStream@odata.mediaEtag\":\"eTag\",\"PropertyStream@odata.mediaContentType\":\"image/jpeg\"}"));
    assertTrue(content.contains("\"@odata.nextLink\""));
    assertTrue(content.contains("ESStreamServerSidePaging?$format=json&%24skiptoken=1%2A10"));
  }
  
  
  @Test
  public void streamESStreamServerSidePagingJsonNext() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$format=json&$skiptoken=1%2A10");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("{\"PropertyInt16\":12,"+
    "\"PropertyStream@odata.mediaEtag\":\"eTag\",\"PropertyStream@odata.mediaContentType\":\"image/jpeg\"}"));
    assertTrue(content.contains("\"@odata.nextLink\""));
    assertTrue(content.contains("ESStreamServerSidePaging?$format=json&%24skiptoken=2%2A10"));
  }
  
  
  @Test
  public void streamCountXml() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$count=true&$format=xml");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.APPLICATION_XML, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());
    assertTrue(content.contains("<a:link rel=\"next\" href="));
    assertTrue(content.contains("ESStreamServerSidePaging?$count=true&amp;$format=xml&amp;%24skiptoken=1%2A10\"/>"));
    assertTrue(content.contains("<a:id>ESStreamServerSidePaging(1)</a:id>"));
    assertTrue(content.contains("<m:count>504</m:count>"));
    assertTrue(content.contains("<d:PropertyInt16 m:type=\"Int16\">1</d:PropertyInt16>"));
    assertTrue(content.contains("<d:PropertyStream m:type=\"Stream\">readLink</d:PropertyStream>"));
  }
  
   
  @Test
  public void streamCountJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$count=true&$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("{\"PropertyInt16\":2,"+
    "\"PropertyStream@odata.mediaEtag\":\"eTag\",\"PropertyStream@odata.mediaContentType\":\"image/jpeg\"}"));
    assertTrue(content.contains("\"@odata.nextLink\""));
    assertTrue(content.contains("ESStreamServerSidePaging?$count=true&$format=json&%24skiptoken=1%2A10"));
    assertTrue(content.contains("\"@odata.count\":504"));
  }
  
  @Test
  public void streamCountFalsetXml() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$count=false&$format=xml");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.APPLICATION_XML, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());
    assertTrue(content.contains("<a:link rel=\"next\" href="));
    assertTrue(content.contains("ESStreamServerSidePaging?$count=false&amp;$format=xml&amp;%24skiptoken=1%2A10\"/>"));
    assertTrue(content.contains("<a:id>ESStreamServerSidePaging(1)</a:id>"));
    assertTrue(content.contains("<d:PropertyInt16 m:type=\"Int16\">1</d:PropertyInt16>"));
    assertTrue(content.contains("<d:PropertyStream m:type=\"Stream\">readLink</d:PropertyStream>"));
    assertFalse(content.contains("<m:count>504</m:count>"));
    }
  
   
  @Test
  public void streamCountFalseJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamServerSidePaging?$count=false&$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("{\"PropertyInt16\":2,"+
    "\"PropertyStream@odata.mediaEtag\":\"eTag\",\"PropertyStream@odata.mediaContentType\":\"image/jpeg\"}"));
    assertTrue(content.contains("\"@odata.nextLink\""));
    assertTrue(content.contains("ESStreamServerSidePaging?$count=false&$format=json&%24skiptoken=1%2A10"));
    assertFalse(content.contains("\"@odata.count\":504"));
    }
  
  @Test
  public void expandStreamPropOnComplexTypeJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamOnComplexProp(7)?$expand=PropertyCompWithStream/PropertyStream,"
        + "PropertyEntityStream,PropertyCompWithStream/NavPropertyETStreamOnComplexPropOne($expand=PropertyStream),"
        + "PropertyCompWithStream/NavPropertyETStreamOnComplexPropMany/$count&$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.setRequestProperty("OData-Version", "4.01");
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("\"NavPropertyETStreamOnComplexPropOne\":{"
        + "\"PropertyInt16\":7,"
        + "\"PropertyStream@mediaEtag\":\"eTag\","
        + "\"PropertyStream@mediaContentType\":\"image/jpeg\","
        + "\"PropertyStream\":\"\ufffdioz\ufffd\\\"\ufffd\"}"));
    assertTrue(content.contains("\"NavPropertyETStreamOnComplexPropMany@count\":2"));
    assertTrue(content.contains("\"PropertyCompWithStream\":{"
        + "\"PropertyStream@mediaEtag\":\"eTag\","
        + "\"PropertyStream@mediaContentType\":\"image/jpeg\","
        + "\"PropertyStream\":\"\ufffdioz\ufffd\\\"\ufffd\","
        + "\"PropertyComp\":{\"PropertyInt16\":333,\"PropertyString\":\"TEST123\"}"));
    assertFalse(content.contains("\"PropertyInt16\":7,"
        + "\"PropertyInt32\":10,"
        + "\"PropertyEntityStream@mediaEtag\":\"eTag\","
        + "\"PropertyEntityStream@mediaContentType\":\"image/jpeg\","
        + "\"PropertyEntityStream\":\"ufffdioz\ufffd\\\"\ufffd\""));
    }
  
  @Test
  public void expandStreamPropOnComplexTypeWithRefJson() throws Exception {
    URL url = new URL(SERVICE_URI + "ESStreamOnComplexProp(7)?$expand="
        + "PropertyCompWithStream/NavPropertyETStreamOnComplexPropMany/$ref&$format=json");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.GET.name());
    connection.setRequestProperty("OData-Version", "4.01");
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));

    final String content = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset());

    assertTrue(content.contains("\"PropertyInt16\":7,"
        + "\"PropertyInt32\":10,\"PropertyEntityStream@mediaEtag\":\"eTag\","
        + "\"PropertyEntityStream@mediaContentType\":\"image/jpeg\","
        + "\"PropertyCompWithStream\":{\"PropertyStream@mediaEtag\":\"eTag\","
        + "\"PropertyStream@mediaContentType\":\"image/jpeg\","
        + "\"PropertyComp\":{\"PropertyInt16\":333,\"PropertyString\":\"TEST123\"},"
        + "\"NavPropertyETStreamOnComplexPropMany\":["
        + "{\"@id\":\"ESWithStream(32767)\"},"
        + "{\"@id\":\"ESWithStream(7)\"}]}"));
    }
  
  @Test
  public void putRequestOnStreamProperty() throws Exception {
    URL url = new URL(SERVICE_URI  + "ESWithStream(7)/PropertyStream");

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod(HttpMethod.PUT.name());
    connection.setRequestProperty(HttpHeader.CONTENT_TYPE, "image/jpeg");
    connection.setRequestProperty(HttpHeader.IF_MATCH, "*");
    connection.setRequestProperty(HttpHeader.ACCEPT, "application/json");
    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("sample.png");
    byte[] bytes = IOUtils.toByteArray(in);
    connection.setDoOutput(true);
    BufferedOutputStream out = new BufferedOutputStream(connection.getOutputStream());
    try {
        out.write(bytes, 0, bytes.length);
        out.flush();
      } finally {
        out.close();
      }
    connection.connect();

    assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
    assertEquals(ContentType.parse("image/jpeg"), 
    		ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));
    assertEquals(bytes.length, IOUtils.toByteArray(connection.getInputStream()).length);
  }
  
  @Override
  protected ODataClient getClient() {
    return null;
  }

}
