/*******************************************************************************
 * 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.odata2.fit.ref;

import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathNotExists;
import static org.junit.Assert.assertFalse;

import org.apache.http.HttpResponse;
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
import org.apache.olingo.odata2.testutil.server.ServletType;
import org.junit.Test;

/**
 * Tests employing the reference scenario reading entity sets in XML format.
 * 
 */
public class FeedXmlReadOnlyTest extends AbstractRefXmlTest {

  public FeedXmlReadOnlyTest(final ServletType servletType) {
    super(servletType);
  }

  @Test
  public void feed() throws Exception {
    HttpResponse response = callUri("Employees()");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    final String payload = getBody(response);
    assertXpathEvaluatesTo("Employees", "/atom:feed/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_1_NAME, "/atom:feed/atom:entry[1]/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:feed/atom:entry[2]/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_3_NAME, "/atom:feed/atom:entry[3]/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_4_NAME, "/atom:feed/atom:entry[4]/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_5_NAME, "/atom:feed/atom:entry[5]/atom:title", payload);
    assertXpathEvaluatesTo(EMPLOYEE_6_NAME, "/atom:feed/atom:entry[6]/atom:title", payload);

    response = callUri("Rooms()");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    assertXpathEvaluatesTo("Rooms", "/atom:feed/atom:title", getBody(response));

    notFound("$top");
    badRequest("Teams/()");
  }

  @Test
  public void navigationFeed() throws Exception {
    HttpResponse response = callUri("Employees('3')/ne_Room/nr_Employees()");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("4", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:feed/atom:entry[1]/atom:title", body);
    assertXpathEvaluatesTo(EMPLOYEE_3_NAME, "/atom:feed/atom:entry[2]/atom:title", body);
    assertXpathEvaluatesTo(EMPLOYEE_4_NAME, "/atom:feed/atom:entry[3]/atom:title", body);
    assertXpathEvaluatesTo(EMPLOYEE_6_NAME, "/atom:feed/atom:entry[4]/atom:title", body);
    assertFalse(body.contains(EMPLOYEE_1_NAME));
    assertFalse(body.contains(EMPLOYEE_5_NAME));

    response = callUri("Rooms('2')/nr_Employees");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    assertXpathEvaluatesTo("4", "count(/atom:feed/atom:entry)", getBody(response));

    response = callUri("Employees('2')/ne_Team/nt_Employees?$orderby=Age&$top=1");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:feed/atom:entry[1]/atom:title", body);
  }

  @Test
  public void skipAndTop() throws Exception {
    HttpResponse response = callUri("Employees?$skip=1&$top=1");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response = callUri("Teams()?$skip=4");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathExists("/atom:feed", body);
    assertXpathNotExists("/atom:feed/atom:entry", body);

    badRequest("Employees?$top=a");
    badRequest("Employees?$top=-1");
    badRequest("Teams('3')?$top=1");
  }

  @Test
  public void skiptoken() throws Exception {
    HttpResponse response = callUri("Employees?$skiptoken=6");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_6_NAME, "/atom:feed/atom:entry[1]/atom:title", body);
    assertFalse(body.contains(EMPLOYEE_1_NAME));

    response = callUri("Container2.Photos?$skiptoken=4foo");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", getBody(response));
  }

  @Test
  public void orderBy() throws Exception {
    HttpResponse response = callUri("Employees?$orderby=EmployeeId%20desc&$skip=5");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_1_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response = callUri("Rooms?$orderby=Seats%20desc,Name&$skip=102");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo("Room 1", "/atom:feed/atom:entry[1]/atom:content/m:properties/d:Name", body);

    badRequest("Employees?$orderby=(id");
    badRequest("Employees?$orderby=id");
  }

  @Test
  public void inlineCount() throws Exception {
    HttpResponse response = callUri("Managers()?$inlinecount=allpages");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("2", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo("2", "/atom:feed/m:count", body);

    response = callUri("Employees()?$top=3&$inlinecount=none");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    assertXpathNotExists("/atom:feed/m:count", getBody(response));

    response = callUri("Rooms('2')/$links/nr_Employees?$skip=9&$inlinecount=allpages");
    checkMediaType(response, HttpContentType.APPLICATION_XML_UTF8);
    assertXpathEvaluatesTo("4", "/d:links/m:count", getBody(response));

    badRequest("Employees()?$top=3&$inlinecount=allpages123");
  }

  @Test
  public void filter() throws Exception {
    HttpResponse response = callUri("Employees?$filter=RoomId%20eq%20%273%27");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_5_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response = callUri("Employees?$filter=EntryDate%20gt%20datetime%272003-12-24T00%3A00%3A00%27");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("2", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_6_NAME, "/atom:feed/atom:entry[2]/atom:title", body);

    response = callUri("Buildings?$filter=Image%20eq%20X%2700%27");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    assertXpathNotExists("/atom:feed/atom:entry", getBody(response));

    response = callUri("Employees?$filter=day(EntryDate)%20eq%20(Age%20mod%208%20add%201)");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response =
        callUri("Employees?$filter=indexof(ImageUrl,EmployeeId)%20mod%20(Age%20sub%2028)%20eq%20month" +
            "(EntryDate)%20mul%203%20div%2027%20sub%201");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_4_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response = callUri("Employees?$filter=not(Age%20sub%2030%20ge%20-hour(EntryDate))");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo(EMPLOYEE_6_NAME, "/atom:feed/atom:entry[1]/atom:title", body);

    response = callUri("Employees('1')/ne_Room/nr_Employees?$filter=EmployeeId%20eq%20'1'");
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", getBody(response));

    response = callUri("Employees('1')/ne_Room/nr_Employees?$filter=EmployeeId%20eq%20'2'");
    assertXpathNotExists("/atom:feed/atom:entry", getBody(response));

    response = callUri("Employees?$filter=Location/City/PostalCode%20lt%20%2769150%27");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertXpathEvaluatesTo("69124", "/atom:feed/atom:entry/m:properties/d:Location/d:City/d:PostalCode", body);

    response = callUri("Employees?$filter=length(trim(Location/City/CityName))%20gt%209");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    body = getBody(response);
    assertXpathEvaluatesTo("1", "count(/atom:feed/atom:entry)", body);
    assertFalse(body.contains(CITY_2_NAME));

    response = callUri("Employees('2')?$filter=Age%20eq%2032");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=entry");
    assertXpathEvaluatesTo(EMPLOYEE_2_NAME, "/atom:entry/atom:title", getBody(response));

    checkUri("Employees('1')/ne_Room/nr_Employees('1')?$filter=EmployeeId%20eq%20'1'");
    checkUri("Container2.Photos(Id=4,Type='foo')?$filter=%D0%A1%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D0%BD%" +
        "D0%B8%D0%B5%20eq%20'%D0%9F%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82'");

    notFound("Employees('4')?$filter=Age%20eq%2099");
    notFound("Rooms('1')/nr_Employees('1')?$filter=Age%20eq%2099");
    notFound("Employees('4')/ne_Room?$filter=Id%20eq%20%271%27");

    badRequest("Employees?$filter=(EmployeeId");
    badRequest("Employees?$filter=(EmployeeId)");
    badRequest("Employees?$filter=loca/city/cityname%20eq%20%27Heidelberg%27");
    badRequest("Employees?$filter=endswith(Location,'y')");
    badRequest("Buildings?$filter=Image%20eq%20X%27notonlyhexdigits%27");

    callUri("Employees?$filter=ne_Manager/Age%20gt%2042", HttpStatusCodes.NOT_IMPLEMENTED);
  }

  @Test
  public void nextLink() throws Exception {
    HttpResponse response = callUri("Rooms()");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("Rooms()?$skiptoken=97", "/atom:feed/atom:link[@rel='next']/@href", body);

    response = callUri("Rooms?$skiptoken=1");
    body = getBody(response);
    assertXpathEvaluatesTo("Rooms?$skiptoken=97", "/atom:feed/atom:link[@rel='next']/@href", body);
  }

  @Test
  public void nextLinkOrderBy() throws Exception {
    HttpResponse response = callUri("Rooms?$orderby=Name");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    String body = getBody(response);
    assertXpathEvaluatesTo("Rooms?$orderby=Name&$skiptoken=97", "/atom:feed/atom:link[@rel='next']/@href", body);
  }

  @Test
  public void nextLinkQueryOptions() throws Exception {
    final HttpResponse response =
        callUri("Rooms?$format=atom&$filter=true&$inlinecount=none&$orderby=Name&$skiptoken=1&$skip=0&$top=200" +
            "&$expand=nr_Building&$select=Seats");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    final String body = getBody(response);
    assertXpathEvaluatesTo(
        "Rooms?$format=atom&$filter=true&$inlinecount=none&$orderby=Name&$top=200&$expand=nr_Building" +
            "&$select=Seats&$skiptoken=97",
        "/atom:feed/atom:link[@rel='next']/@href", body);
  }

  @Test
  public void nextLinkNavigation() throws Exception {
    // We have to create one entry to have one more than the paging size.
    final String requestBody = getBody(callUri("Rooms('1')")).replaceAll("<link.+?/>", "");
    HttpResponse response =
        postUri("Buildings('3')/nb_Rooms", requestBody, HttpContentType.APPLICATION_ATOM_XML_ENTRY,
            HttpStatusCodes.CREATED);
    getBody(response);

    response = callUri("Buildings('3')/nb_Rooms");
    checkMediaType(response, HttpContentType.APPLICATION_ATOM_XML_UTF8 + ";type=feed");
    final String body = getBody(response);
    assertXpathEvaluatesTo("Buildings('3')/nb_Rooms?$skiptoken=99", "/atom:feed/atom:link[@rel='next']/@href", body);
  }
}
