// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed 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 adwords.axis.v201809.extensions;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;

import adwords.axis.v201809.basicoperations.AddCampaigns;
import com.beust.jcommander.Parameter;
import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.v201809.cm.ApiError;
import com.google.api.ads.adwords.axis.v201809.cm.ApiException;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignExtensionSetting;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignExtensionSettingOperation;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignExtensionSettingReturnValue;
import com.google.api.ads.adwords.axis.v201809.cm.CampaignExtensionSettingServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.DayOfWeek;
import com.google.api.ads.adwords.axis.v201809.cm.ExtensionFeedItem;
import com.google.api.ads.adwords.axis.v201809.cm.ExtensionSetting;
import com.google.api.ads.adwords.axis.v201809.cm.FeedItemDevicePreference;
import com.google.api.ads.adwords.axis.v201809.cm.FeedItemGeoRestriction;
import com.google.api.ads.adwords.axis.v201809.cm.FeedItemSchedule;
import com.google.api.ads.adwords.axis.v201809.cm.FeedItemScheduling;
import com.google.api.ads.adwords.axis.v201809.cm.FeedType;
import com.google.api.ads.adwords.axis.v201809.cm.GeoRestriction;
import com.google.api.ads.adwords.axis.v201809.cm.Keyword;
import com.google.api.ads.adwords.axis.v201809.cm.KeywordMatchType;
import com.google.api.ads.adwords.axis.v201809.cm.Location;
import com.google.api.ads.adwords.axis.v201809.cm.MinuteOfHour;
import com.google.api.ads.adwords.axis.v201809.cm.Operator;
import com.google.api.ads.adwords.axis.v201809.cm.SitelinkFeedItem;
import com.google.api.ads.adwords.axis.v201809.cm.UrlList;
import com.google.api.ads.adwords.axis.v201809.mcm.Customer;
import com.google.api.ads.adwords.axis.v201809.mcm.CustomerServiceInterface;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.utils.examples.ArgumentNames;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.ads.common.lib.utils.examples.CodeSampleParams;
import com.google.api.client.auth.oauth2.Credential;
import java.rmi.RemoteException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * This example adds sitelinks to a campaign. To create a campaign, run {@link AddCampaigns}.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class AddSiteLinks {

  private static class AddSiteLinksParams extends CodeSampleParams {
    @Parameter(names = ArgumentNames.CAMPAIGN_ID, required = true)
    private Long campaignId;
  }

  public static void main(String[] args) {
    AdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an AdWordsSession.
      session =
          new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential).build();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    AddSiteLinksParams params = new AddSiteLinksParams();
    if (!params.parseArguments(args)) {
      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.campaignId = Long.parseLong("INSERT_CAMPAIGN_ID_HERE");
    }

    try {
      runExample(adWordsServices, session, params.campaignId);
    } catch (ApiException apiException) {
      // ApiException is the base class for most exceptions thrown by an API request. Instances
      // of this exception have a message and a collection of ApiErrors that indicate the
      // type and underlying cause of the exception. Every exception object in the adwords.axis
      // packages will return a meaningful value from toString
      //
      // ApiException extends RemoteException, so this catch block must appear before the
      // catch block for RemoteException.
      System.err.println("Request failed due to ApiException. Underlying ApiErrors:");
      if (apiException.getErrors() != null) {
        int i = 0;
        for (ApiError apiError : apiException.getErrors()) {
          System.err.printf("  Error %d: %s%n", i++, apiError);
        }
      }
    } catch (RemoteException re) {
      System.err.printf(
          "Request failed unexpectedly due to RemoteException: %s%n", re);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @param campaignId the ID of the campaign where sitelinks will be added.
   * @throws ApiException if the API request failed with one or more service errors.
   * @throws RemoteException if the API request failed due to other errors.
   */
  public static void runExample(AdWordsServicesInterface adWordsServices, AdWordsSession session,
      Long campaignId) throws ApiException, RemoteException {
    // Get the CustomerService.
    CustomerServiceInterface customerService =
        adWordsServices.get(session, CustomerServiceInterface.class);

    // Find the matching customer and its time zone. The getCustomers method will return
    // a single Customer object corresponding to the session's clientCustomerId.
    Customer customer = customerService.getCustomers()[0];
    DateTimeZone customerTimeZone = DateTimeZone.forID(customer.getDateTimeZone());
    System.out.printf(
        "Found customer ID %d with time zone '%s'.%n",
        customer.getCustomerId(), customer.getDateTimeZone());

    // Get the CampaignExtensionSettingService.
    CampaignExtensionSettingServiceInterface campaignExtensionSettingService =
        adWordsServices.get(session, CampaignExtensionSettingServiceInterface.class);

    // Create the sitelinks.
    SitelinkFeedItem sitelink1 =
        createSiteLinkFeedItem("Store Hours", "http://www.example.com/storehours");

    // Show the Thanksgiving specials link only from 20 - 27 Nov.
    SitelinkFeedItem sitelink2 =
        createSiteLinkFeedItem("Thanksgiving Specials", "http://www.example.com/thanksgiving");

    // The time zone of the start and end date/times must match the time zone of the customer.
    DateTime startTime = new DateTime(DateTime.now().getYear(), 11, 20, 0, 0, 0, customerTimeZone);
    if (startTime.isBeforeNow()) {
      // Move the startTime to next year if the current date is past November 20th.
      startTime = startTime.plusYears(1);
    }
    sitelink2.setStartTime(startTime.toString("yyyyMMdd HHmmss ZZZ"));
    // Use the same year as startTime when creating endTime.
    DateTime endTime = new DateTime(startTime.getYear(), 11, 27, 23, 59, 59, customerTimeZone);
    sitelink2.setEndTime(endTime.toString("yyyyMMdd HHmmss ZZZ"));

    // Target this sitelink for United States only. See
    // https://developers.google.com/adwords/api/docs/appendix/geotargeting
    // for valid geolocation codes.
    Location unitedStates = new Location();
    unitedStates.setId(2840L);
    sitelink2.setGeoTargeting(unitedStates);

    // Restrict targeting only to people physically within the United States.
    // Otherwise, this could also show to people interested in the United States
    // but not physically located there.
    FeedItemGeoRestriction geoTargetingRestriction = new FeedItemGeoRestriction();
    geoTargetingRestriction.setGeoRestriction(GeoRestriction.LOCATION_OF_PRESENCE);
    sitelink2.setGeoTargetingRestriction(geoTargetingRestriction);

    // Show the wifi details primarily for high end mobile users.
    SitelinkFeedItem sitelink3 =
        createSiteLinkFeedItem("Wifi available", "http://www.example.com/mobile/wifi");
    // See https://developers.google.com/adwords/api/docs/appendix/platforms for device criteria
    // IDs.
    FeedItemDevicePreference devicePreference = new FeedItemDevicePreference(30001L);
    sitelink3.setDevicePreference(devicePreference);

    // Target this sitelink for the keyword "free wifi".
    Keyword wifiKeyword = new Keyword();
    wifiKeyword.setText("free wifi");
    wifiKeyword.setMatchType(KeywordMatchType.BROAD);
    sitelink3.setKeywordTargeting(wifiKeyword);

    // Show the happy hours link only during Mon - Fri 6PM to 9PM.
    SitelinkFeedItem sitelink4 =
        createSiteLinkFeedItem("Happy hours", "http://www.example.com/happyhours");
    sitelink4.setScheduling(new FeedItemScheduling(new FeedItemSchedule[] {
        new FeedItemSchedule(DayOfWeek.MONDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
        new FeedItemSchedule(DayOfWeek.TUESDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
        new FeedItemSchedule(DayOfWeek.WEDNESDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
        new FeedItemSchedule(DayOfWeek.THURSDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO),
        new FeedItemSchedule(DayOfWeek.FRIDAY, 18, MinuteOfHour.ZERO, 21, MinuteOfHour.ZERO)}));

    // Create your campaign extension settings. This associates the sitelinks
    // to your campaign.
    CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
    campaignExtensionSetting.setCampaignId(campaignId);
    campaignExtensionSetting.setExtensionType(FeedType.SITELINK);
    ExtensionSetting extensionSetting = new ExtensionSetting();
    extensionSetting.setExtensions(
        new ExtensionFeedItem[] {sitelink1, sitelink2, sitelink3, sitelink4});
    campaignExtensionSetting.setExtensionSetting(extensionSetting);

    CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation();
    operation.setOperand(campaignExtensionSetting);
    operation.setOperator(Operator.ADD);

    // Add the extensions.
    CampaignExtensionSettingReturnValue returnValue =
        campaignExtensionSettingService.mutate(new CampaignExtensionSettingOperation[] {operation});
    if (returnValue.getValue() != null && returnValue.getValue().length > 0) {
      CampaignExtensionSetting newExtensionSetting = returnValue.getValue(0);
      System.out.printf("Extension setting with type '%s' was added to campaign ID %d.%n",
          newExtensionSetting.getExtensionType().getValue(), newExtensionSetting.getCampaignId());
    } else {
      System.out.println("No extension settings were created.");
    }
  }

  /**
   * Creates a new {@link SitelinkFeedItem} with the specified attributes.
   *
   * @param sitelinkText the text for the sitelink
   * @param sitelinkUrl the URL for the sitelink
   * @return a new SitelinkFeedItem
   */
  private static SitelinkFeedItem createSiteLinkFeedItem(String sitelinkText, String sitelinkUrl) {
    SitelinkFeedItem sitelinkFeedItem = new SitelinkFeedItem();
    sitelinkFeedItem.setSitelinkText(sitelinkText);
    sitelinkFeedItem.setSitelinkFinalUrls(new UrlList(new String[] {sitelinkUrl}));
    return sitelinkFeedItem;
  }

}