import java.util.regex.Matcher
import java.util.regex.Pattern

apply plugin: 'com.android.application'

android {
  compileSdkVersion 28
  buildToolsVersion '28.0.3'

  compileOptions {
    targetCompatibility 1.8
    sourceCompatibility 1.8
  }

  defaultConfig {
    applicationId "se.leap.bitmaskclient"
    versionCode 147
    versionName "1.0.3"
    minSdkVersion 16
    targetSdkVersion 28
    vectorDrawables.useSupportLibrary = true
    buildConfigField 'boolean', 'openvpn3', 'false'


    //Build Config Fields for default donation details

    //This is the default donation URL and should be set to the donation page of LEAP
    // and this should not be set/altered anywhere else.
    buildConfigField 'String', 'default_donation_url', '"https://leap.se/en/about-us/donate"'
    //This is the donation URL and should be set to the relevant donation page.
    buildConfigField 'String', 'donation_url', 'null'
    //The field to enable donations in the app.
    buildConfigField 'boolean', 'enable_donation', 'true'
    //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled.
    buildConfigField 'boolean', 'enable_donation_reminder', 'true'
    //The duration in days to trigger the donation reminder
    buildConfigField 'int', 'donation_reminder_duration', '30'
    //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead
    buildConfigField 'boolean', 'priotize_anonymous_usage', 'false'
    //ignore the following configs, only used in custom flavor
    buildConfigField "String", "customProviderUrl", '""'
    buildConfigField "String", "customProviderIp", '""'
    buildConfigField "String", "customProviderApiIp", '""'

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    dexOptions {
      jumboMode true
      multiDexEnabled true
    }
  }

  signingConfigs {
    release {
      storeFile project.hasProperty('storeFileProperty') ? file(storeFileProperty) : null
      storePassword project.hasProperty('storePasswordProperty') ?  storePasswordProperty : ""
      keyAlias project.hasProperty('keyAliasProperty') ?  keyAliasProperty : ""
      keyPassword project.hasProperty('keyPasswordProperty') ?  keyPasswordProperty : ""
    }
  }

  flavorDimensions "branding", "implementation", "abi"
  productFlavors {
    productFlavors.all {
      ext.appName = null
      ext.splitApk = false
    }
    production {
      dimension "implementation"
    }
    insecure {
      dimension "implementation"
    }

    normal {
      dimension "branding"
      appName = "Bitmask"
      splitApk = true
    }

    custom {
      dimension "branding"

      //**************************************************************************
      //**************************************************************************
      //Configurations for custom branded app.

      //Change the package name as needed, e.g. "org.example.myapp"
      applicationId "se.leap.riseupvpn"
      //Set app name here
      appName = "Riseup VPN"
      //Provider base url, e.g. '"https://example.com"'
      def customProviderUrl = '"https://riseup.net"'
      buildConfigField "String", "customProviderUrl", customProviderUrl
      //static ip address of provider, using a commercially validated CA certificate to serve the provider.json
      def customProviderIp = '"198.252.153.70"'
      buildConfigField "String", "customProviderIp", customProviderIp
      //static ip address of the provider api, using a self signed certificate to serve provider.json, eip-service.json etc.
      def customProviderApiIp = '"198.252.153.107"'
      buildConfigField "String", "customProviderApiIp", customProviderApiIp
      //Change the versionCode as needed
      //versionCode 1
      //Change the versionName as needed
      //versionName "0.9.9RC1"

      //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead
      buildConfigField 'boolean', 'priotize_anonymous_usage', 'true'

      //Build Config Fields for default donation details

      //This is the donation URL and should be set to the relevant donation page.
      buildConfigField 'String', 'donation_url', '"https://riseup.net/vpn/donate"'
      //The field to enable donations in the app.
      buildConfigField 'boolean', 'enable_donation', 'true'
      //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled.
      buildConfigField 'boolean', 'enable_donation_reminder', 'true'
      //The duration in days to trigger the donation reminder
      buildConfigField 'int', 'donation_reminder_duration', '30'

      // Build apks for each architecture, in addition to one 'fat' apk containing libraries for all all architectures
      // enable this if you're publishing in gplay
      ext {
        splitApk = false
      }

      //**************************************************************************
      //**************************************************************************

    }


    fat {
      dimension "abi"
      ext {
        abiVersionCode = 0
        abiFilter = ""
      }
    }

    x86 {
      dimension "abi"
      ndk {
        abiFilters "x86"
      }
      ext {
        abiVersionCode = 1
        abiFilter = "x86"
      }
    }

    armv7 {
      dimension "abi"
      ndk {
        abiFilters "armeabi-v7a"
      }
      ext {
        abiVersionCode = 2
        abiFilter = "armeabi-v7a"
      }
    }

    x86_64 {
      dimension "abi"
      ndk {
        abiFilters "x86_64"
      }
      ext {
        abiVersionCode = 3
        abiFilter = "x86_64"
      }
    }

    arm64 {
      dimension "abi"
      ndk {
        abiFilters "arm64-v8a"
      }
      ext {
        abiVersionCode = 4
        abiFilter = "arm64-v8a"
      }
    }
  }

  buildTypes {
    buildTypes.all {
      ext.appSuffix = ""
    }
    release {
      //runProguard true
      if(signingConfigs.contains(release))
        signingConfig signingConfigs.release.isSigningReady() ? signingConfigs.release : signingConfigs.debug
      //minifyEnabled = true
      //shrinkResources true
    }
    beta {
      initWith release
      applicationIdSuffix ".beta"
      appSuffix = " Beta"
    }
    debug {
      testCoverageEnabled = true
    }
  }

  lintOptions {
    abortOnError false
  }

  sourceSets {
    main {
      assets.srcDirs = ['assets',
                        'ovpnlibs/assets',
                        '../ics-openvpn/main/build/ovpnassets',
                        // '../go/out' TODO: uncomment this line as soon as we want to use PT in production
                        ]
      jniLibs.srcDirs = ['../ics-openvpn/main/build/intermediates/cmake/noovpn3/release/obj']
      jni.srcDirs = [] //disable automatic ndk-build
    }
    debug {
      assets.srcDirs = ['src/debug/assets',
                        /*'../go/out'*/]
    }

    test {
      resources.srcDirs += ['src/test/resources']
      java.srcDirs += ['src/sharedTest/java']
    }

    androidTest {
      java.srcDirs += ['src/sharedTest/java']
    }
  }

  /**
   * BUILD VARAIANTS:
   * =================
   * Development builds:
   * --------------------
   * customProductionFatDebug -> branded development build, includes all ABIs
   * normalProductionFatDebug -> Bitmask development build, includes all ABIS
   * customInsecureFatDebug -> branded development build, doesn't checks certificates (for test server setup w/o valid certificates), includes all ABIs
   * normalInsecureFatDebug -> Bitmask development build, doesn't checks certificates (for test server setup w/o valid certificates), includes all ABIs
   *
   * Branded Releases:
   * -----------------
   * customProductionFatBeta -> branded build, includes all ABI's, Beta release
   * customProductionFatRelease -> branded build, includes all ABI's, stable release (-> F-Droid, GPlay if not splitApk is set to true)
   *
   * Bitmask Beta releases:
   * ----------------------
   * normalProductionArm64Beta  ->  Bitmask build, only for ABI arm64, for GPlay Beta channel with split apks (1 of 4)
   * normalProductionArmv7Beta  ->  Bitmask build, only for ABI armeabi-v7a, for GPlay Beta channel with split apks (2 of 4)
   * normalProductionX86Beta    ->  Bitmask build, only for ABI x86, for GPlay Beta channel with split apks (3 of 4)
   * normalProductionX86_64Beta ->  Bitmask build, only for ABI x86 64 bit, for GPlay Beta channel with split apks (4 of 4)
   * normalProductionFatBeta    ->  Bitmask build, including all ABIS, for izzysoft's F-Droid repo and beta link on download page
   *
   * Bitmask Stable releases:
   * ------------------------
   * normalProductionArm64Release  ->  Bitmask build, only for ABI arm64, for GPlay releases with split apks (1 of 4)
   * normalProductionArmv7Release  ->  Bitmask build, only for ABI armeabi-v7a, for GPlay releases with split apks (2 of 4)
   * normalProductionX86Release    ->  Bitmask build, only for ABI x86, for GPlay releases with split apks (3 of 4)
   * normalProductionX86_64Release ->  Bitmask build, only for ABI x86 64 bit, for GPlay releases with split apks (4 of 4)
   * normalProductionFatRelease    ->  Bitmask build, including all ABIS, for official F-Droid repo and stable link on download page
   */

  variantFilter { variant ->

    def names = variant.flavors*.name
    def buildTypeName = variant.buildType.name
    // flavorDimensions "branding" -> 0, "implementation" -> 1, "abi" -> 2
    def supportsSplitApk = variant.flavors[0].splitApk
    // To check for a certain build type, use variant.buildType.name == "<buildType>"
    if (((names.contains("insecure") && !names.contains("fat")) ||
            (names.contains("insecure") && buildTypeName.contains("beta")) ||
            (names.contains("insecure") && buildTypeName.contains("release")) ||
            (buildTypeName.contains("debug") && !names.contains("fat")) ||
            (!supportsSplitApk && !names.contains("fat")))
    ) {
      // Gradle ignores any variants that satisfy the conditions above.
      setIgnore(true)
    }
  }
}




dependencies {
  testImplementation 'junit:junit:4.12'
  //outdated mockito-core version due to powermock dependency
  testImplementation 'org.mockito:mockito-core:2.8.9'
  testImplementation('org.powermock:powermock-api-mockito2:1.7.3')
          { exclude group: 'junit' exclude group: 'org.mockito' }
  testImplementation 'org.powermock:powermock-module-junit4:1.7.3'
  testImplementation 'org.powermock:powermock-core:1.7.3'
  testImplementation 'org.powermock:powermock-module-junit4-rule:1.7.3'
  testImplementation group: 'com.tngtech.java', name: 'junit-dataprovider', version: '1.10.0'

  androidTestImplementation 'org.mockito:mockito-core:2.8.9'
  androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
  androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
  androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2'
  //TODO: remove that library
  androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.6.3'
  testImplementation 'junit:junit:4.12'
  testImplementation 'org.json:json:20170516'
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
  betaImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
  annotationProcessor 'com.jakewharton:butterknife:6.1.0'
  annotationProcessor 'com.squareup.dagger:dagger-compiler:1.2.2'
  implementation 'com.jakewharton:butterknife:6.1.0'
  //TODO: replace that library
  compileOnly 'com.squareup.dagger:dagger-compiler:1.2.2'
  implementation 'com.github.pedrovgs:renderers:1.5'
  implementation 'com.intellij:annotations:12.0'
  implementation 'com.google.code.gson:gson:2.8.2'
  implementation 'com.squareup.okhttp3:okhttp:3.9.0'
  implementation "com.android.support:support-core-utils:28.0.0"
  implementation 'com.android.support:support-annotations:28.0.0'
  implementation 'com.android.support:support-v4:28.0.0'
  implementation 'com.android.support:appcompat-v7:28.0.0'
  implementation 'com.android.support:design:28.0.0'
  implementation 'com.android.support:support-fragment:28.0.0'
  implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  implementation 'com.android.support:multidex:1.0.3'
  implementation 'com.android.support:cardview-v7:28.0.0'
  implementation project(path: ':shapeshifter')
}

android.applicationVariants.all { variant ->
  // configure app name and apk file name for different build variants
  def flavors = variant.productFlavors
  // flavorDimensions "branding" -> 0, "implementation" -> 1, "abi" -> 2
  def branding = flavors[0]
  def trimmedAppName = branding.appName.replaceAll(' ', '')
  def abiDimension = flavors[2]
  def abiFilter = abiDimension.abiFilter
  if (abiFilter.length() > 0) {
    abiFilter = "_"+abiFilter
  }
  def buildType = variant.buildType
  def tag = getTag()
  if (tag.length() > 0) {
    tag = "_"+tag
  }

  variant.resValue "string", "app_name",  "\"${branding.appName}${buildType.appSuffix}\""

  variant.outputs.all { output ->
    output.outputFileName = "${trimmedAppName}${abiFilter}_${buildType.name}${tag}.apk"
  }

  // reconfigure version codes for split builds
  variant.outputs.each { output ->
      // if not a fat build
      if (abiDimension.abiVersionCode > 0) {
          output.versionCodeOverride = android.defaultConfig.versionCode * 1000 + abiDimension.abiVersionCode
      }
  }

  // remove unrelated abi specific assets
  variant.mergeAssets.doLast {
    // if not a fat build
    if (abiDimension.abiVersionCode > 0) {
      def filesToDelete = fileTree(dir: variant.mergeAssets.outputDir, excludes: ["*pie_openvpn.${abiDimension.abiFilter}",
                                                                                  'urls/',
                                                                                  '*.url',
                                                                                  '*.json',
                                                                                  '*.pem'])
      delete(filesToDelete)
    }
  }
}


// Ensure the no-op dependency is always used in JVM tests.
configurations.all { config ->
  if (config.name.contains('UnitTest')) {
    config.resolutionStrategy.eachDependency { details ->
      if (details.requested.group == 'com.squareup.leakcanary' && details.requested.name == 'leakcanary-android') {
        details.useTarget(group: details.requested.group, name: 'leakcanary-android-no-op', version: details.requested.version)
      }
    }
  }
  resolutionStrategy.force "com.android.support:support-annotations:28.0.0"
  resolutionStrategy.force "com.android.support:support-v4:28.0.0"
  resolutionStrategy.force "com.android.support:support-core-utils:28.0.0"
  resolutionStrategy.force "com.android.support:appcompat-v7:28.0.0"
  resolutionStrategy.force "com.android.support:design:28.0.0"
  resolutionStrategy.force "com.android.support:support-fragment:28.0.0"
}

subprojects {
  afterEvaluate {project ->
    if (project.hasProperty("android")) {
      android {
        compileSdkVersion 28
        buildToolsVersion "28.0.3"
      }
    }
  }
}

def processFileInplace(file, Closure processText) {
  def text = file.text
  file.write(processText(text))
}

task copyIcsOpenVPNClasses(dependsOn: 'copyIcsOpenVPNFiles', type: Copy  ) {
  println "copyIcsOpenVPNClasses"
//  from ('../ics-openvpn/main/') {
//    include '**/LaunchVPN.java'
//    include '**/OpenVPNSservice.java'
//    include '**/VpnProfile.java'
//    include '**/DisconnectVPN.java'
//    include '**/VpnProfile.java'
//    include '**/SeekBarTicks.java'
//    include '**/core/**.java'
//    include '**/activities/BaseActivity.java'
//    include '**/APIVpnProfile.java'
//    include '**/aidl/**/api/**.aidl'
//    include '**/aidl/**/core/**.aidl'
//
//    includeEmptyDirs = false
//
//    filter {
//      line -> line.replaceAll('de.blinkt.openvpn.R', 'se.leap.bitmaskclient.R')
//    }
//    filter {
//      line -> line.replaceAll('de.blinkt.openvpn.BuildConfig', 'se.leap.bitmaskclient.BuildConfig')
//    }
//    filter {
//      line -> line.replace('package de.blinkt.openvpn;', 'package de.blinkt.openvpn;\n\nimport se.leap.bitmaskclient.R;')
//    }
//  } into '.'
}

task copyIcsOpenVPNXml(dependsOn: 'copyIcsOpenVPNFiles', type: Copy )  {
  println "copyIcsOpenVPNXml"
  from ('../ics-openvpn/main/') {
    include '**/strings.xml'
    include '**/refs.xml'
    include '**/white_rect.xml'
    include '**/plurals.xml'
    includeEmptyDirs = false

    rename 'strings.xml', 'strings-icsopenvpn.xml'
    rename 'plurals.xml', 'plurals-icsopenvpn.xml'
    filter {
      line -> line.replaceAll('.*name="app".*', '')
    }
  } into '.'
}

task copyIcsOpenVPNImages(dependsOn: 'copyIcsOpenVPNFiles', type: Copy ) {
  println "copyIcsOpenVPNImages"
//  from ('../ics-openvpn/main/') {
//    include '**/ic_filter*.png'
//    include '**/ic_delete*.png'
//    include '**/ic_share*.png'
//    include '**/ic_close*.png'
//    include '**/ic_edit*.png'
//    include '**/ic_check*.png'
//    include '**/ic_pause*.png'
//    include '**/ic_play*.png'
//    include '**/ic_content_copy_white_*.png'
//    include '**/ic_add_circle_outline_white_*.png'
//    include '**/ic_warning_black_*.png'
//    include '**/ic_add_circle_outline_grey600_*.png'
//    include '**/ic_archive_grey600_*.png'
//    include '**/ic_receipt_white_*.png'
//    include '**/ic_sort_white_*.png'
//    include '**/ic_content_copy_white_*.png'
//    include '**/ic_archive_white_*.png'
//    include '**/ic_menu_archive*.png'
//    include '**/vpn_item_settings*.png'
//    include '**/ic_menu_log*.png'
//    include '**/ic_menu_copy_holo_light*.png'
//
//    includeEmptyDirs = false
//  } into '.'
}

// thanks to http://pleac.sourceforge.net/pleac_groovy/fileaccess.html
def removeDuplicatedStrings() {
  println "removeDuplicatedStrings"
  new File('.').eachFileRecurse {
    if(it.name.equals('strings.xml') || it.name.equals('plurals.xml')) {
      replaceDuplicatesForSource(it, it.name.substring(0, it.name.lastIndexOf('.')))
    }
  }
}

def replaceDuplicatesForSource(File it, String type) {
  def ics_openvpn_file = file(it.absolutePath.replace(type+'.xml', type+'-icsopenvpn.xml'))
  if(ics_openvpn_file.exists()) {
    def ics_openvpn_strings_names = new XmlParser().parse(ics_openvpn_file)
    def current_file = it

    ics_openvpn_strings_names.string.each {
      processFileInplace(current_file) { text ->
        text.replaceAll('.*name=\"' + it.attribute('name') + '\".*(\n)*.*string>.*\n+', '')
      }
    }
  }
}

task mergeUntranslatable(dependsOn: 'copyIcsOpenVPNFiles',  type: Copy ) {
  println "mergeUntranslatable"
  from ('../ics-openvpn/main/') {
    include '**/untranslatable.xml'
    rename 'untranslatable.xml', 'untranslatable-icsopenvpn.xml'
  } into '.'
  def bitmask_untranslatable = file('src/main/res/values/untranslatable.xml')
  def ics_openvpn_untranslatable = new File(bitmask_untranslatable.path.replace('untranslatable.xml', 'untranslatable-icsopenvpn.xml'))
  ics_openvpn_untranslatable.createNewFile()
  def string_continuation = false;
  ics_openvpn_untranslatable.eachLine { text ->
    if(text.contains('string name=')) {
      if(!bitmask_untranslatable.text.contains(text))
        bitmask_untranslatable << text
      if(text.contains('</string>'))
        string_continuation = true
    }
    else if(string_continuation) {
      bitmask_untranslatable << text
    }

    if(text.contains('</string>')) {
      string_continuation = false
      bitmask_untranslatable << System.getProperty("line.separator")
    }
  }

  bitmask_untranslatable.write(bitmask_untranslatable.text.replaceAll("</resources>", ""))
  bitmask_untranslatable << "</resources>"

  delete ics_openvpn_untranslatable
}

task copyIcsOpenVPNFiles(dependsOn: 'updateIcsOpenVpn') {
  println "copyIcsOpenVPNFiles"

  copyIcsOpenVPNClasses
  copyIcsOpenVPNXml
  copyIcsOpenVPNImages
  //mergeUntranslatable.execute()
  removeDuplicatedStrings()
}

task updateIcsOpenVpn( type: Exec ) {
  commandLine 'git', 'submodule', 'sync'
  commandLine 'git', 'submodule', 'update', '--init', '--recursive'

}

def getTag() {
  String commit = "git log --pretty=format:'%h' -n 1".execute().text.trim().replaceAll("'", "")
  return ("git describe --tags --exact-match "+ commit).execute().text.trim()
}


task cleanNative( type: Delete ) {
  def shouldClean = getCurrentFlavorForBetaOrRelease() == "production"
  println  "cleanNative: " + shouldClean

  if (shouldClean) {
    def dirName = "obj"
    file( dirName ).list().each{
      f -> delete "${dirName}/${f}"
    }
  }
}

task updateSdkLicences ( type: Exec ) {
  println "say yes to licenses"
  commandLine 'sh', 'yes', '|', 'sdkmanager', '--licenses'
}

def getCurrentFlavorForBetaOrRelease() {
  Gradle gradle = getGradle()
  String  tskReqStr = gradle.getStartParameter().getTaskRequests().toString()

  Pattern pattern;

  if( tskReqStr.contains( "assemble" ) )
    pattern = Pattern.compile("assemble(\\w+)(Beta|Release)")
  else
    pattern = Pattern.compile("generate(\\w+)(Beta|Release)")

  Matcher matcher = pattern.matcher( tskReqStr )

  if( matcher.find() )
    return matcher.group(1).toLowerCase()
  else
  {
    return "";
  }
}