package au.com.southsky.jfreesane;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.io.CharSource;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Represents the authentication configuration used by SANE clients. The SANE utilities like
 * {@code scanimage} will read the {@code ~/.sane/pass} directory (if it exists), this class
 * provides an implementation of that behavior.
 *
 * <p>
 * Threadsafe.
 */
public class SaneClientAuthentication extends SanePasswordProvider {
  private static final Logger logger = Logger.getLogger(SaneClientAuthentication.class.getName());

  public static final String MARKER_MD5 = "$MD5$";

  private static final String DEFAULT_CONFIGURATION_PATH =
      Joiner.on(File.separator).join(System.getProperty("user.home"), ".sane", "pass");

  private final List<String> resources;
  private final List<String> usernames;
  private final List<String> passwords;

  private final CharSource configurationSource;
  private boolean initialized = false;

  public SaneClientAuthentication() {
    this(DEFAULT_CONFIGURATION_PATH);
  }

  public SaneClientAuthentication(final String path) {
    this(
        new CharSource() {
          @Override
          public Reader openStream() throws IOException {
            return new InputStreamReader(new FileInputStream(path), StandardCharsets.US_ASCII);
          }
        });
  }

  /**
   * Returns a new {@code SaneClientAuthentication} whose configuration is represented by the
   * characters supplied by the given {@link CharSource}.
   */
  public SaneClientAuthentication(CharSource configurationSource) {
    this.configurationSource = configurationSource;
    this.resources = new ArrayList<>(4);
    this.usernames = new ArrayList<>(4);
    this.passwords = new ArrayList<>(4);
  }

  private synchronized void initializeIfRequired() {
    if (initialized) {
      return;
    }

    initialized = true;
    try (BufferedReader r = configurationSource.openBufferedStream()) {
      String line;

      int lineNumber = 0;
      while ((line = r.readLine()) != null) {
        lineNumber++;
        ClientCredential credential = ClientCredential.fromAuthString(line);
        if (credential == null) {
          logger.log(
              Level.WARNING,
              "ignoring invalid configuration format (line {0}): {1}",
              new Object[] {lineNumber, line});
        } else {
          resources.add(credential.backend);
          usernames.add(credential.username);
          passwords.add(credential.password);
        }
      }
    } catch (IOException e) {
      logger.log(Level.WARNING, "could not read auth configuration due to IOException", e);
    }
  }

  /**
   * Returns {@code true} if the configuration contains an entry for the given resource.
   */
  @Override
  public boolean canAuthenticate(String resource) {
    if (resource == null) {
      return false;
    }

    ClientCredential credential = getCredentialForResource(resource);
    return credential != null;
  }

  public ClientCredential getCredentialForResource(String rc) {
    initializeIfRequired();
    String resource = rc.contains(MARKER_MD5) ? rc.substring(0, rc.indexOf(MARKER_MD5)) : rc;

    int idx = resources.indexOf(resource);
    if (idx != -1) {
      return new ClientCredential(resources.get(idx), usernames.get(idx), passwords.get(idx));
    }

    return null;
  }

  @Override
  public String getUsername(String resource) {
    return getCredentialForResource(resource).username;
  }

  @Override
  public String getPassword(String resource) {
    return getCredentialForResource(resource).password;
  }

  /**
   * Class to hold Sane client credentials organised by backend.
   *
   * @author paul
   */
  public static class ClientCredential {
    private final String backend;
    private final String username;
    private final String password;

    protected ClientCredential(String backend, String username, String password) {
      this.backend = backend;
      this.username = username;
      this.password = password;
    }

    public static ClientCredential fromAuthString(String authString) {
      List<String> fields = Splitter.on(":").splitToList(authString);
      if (fields.size() < 3) {
        return null;
      }

      return new ClientCredential(fields.get(2), fields.get(0), fields.get(1));
    }

    public String getBackend() {
      return backend;
    }

    public String getUsername() {
      return username;
    }

    public String getPassword() {
      return password;
    }
  }
}

@@ -58,151 +58,68 @@
 mon.
-base.Joiner;%0Aimport com.google.common.base.Splitter;%0Aimport com.google.common.io.CharSource;%0A%0Aimport java.io.BufferedReader;%0Aimport java.io.Fil
+io.CharSource;%0A%0Aimport javax.annotation.concurrent.ThreadSaf
 e;%0Ai
@@ -132,31 +132,30 @@
 java.io.
-FileInputStream
+BufferedReader
 ;%0Aimport
@@ -275,16 +275,99 @@
 arsets;%0A
+import java.nio.file.Files;%0Aimport java.nio.file.Path;%0Aimport java.nio.file.Paths;%0A
 import j
@@ -719,34 +719,21 @@
 .%0A *
-%0A * %3Cp%3E%0A * 
+/%0A@
 Thread
-s
+S
 afe
-.%0A */
 %0Apub
@@ -973,22 +973,20 @@
 c final 
-String
+Path
  DEFAULT
@@ -1017,38 +1017,17 @@
     
-Joiner.on(File.separator).join
+Paths.get
 (Sys
@@ -1416,24 +1416,109 @@
 ing path) %7B%0A
+    this(Paths.get(path));%0A  %7D%0A%0A  public SaneClientAuthentication(final Path path) %7B%0A
     this(%0A  
@@ -1665,16 +1665,17 @@
 der(
-new 
 File
+s.new
 Inpu
@@ -4503,69 +4503,88 @@
     
-List%3CString%3E fields = Splitter.on(%22:%22).splitToList(authString
+@SuppressWarnings(%22StringSplitter%22)%0A      String%5B%5D fields = authString.split(%22:%22
 );%0A 
@@ -4603,14 +4603,14 @@
 lds.
-size()
+length
  %3C 3
@@ -4687,23 +4687,19 @@
 elds
-.get(2)
+%5B2%5D
 , fields
 .get
@@ -4698,23 +4698,19 @@
 elds
-.get(0)
+%5B0%5D
 , fields
 .get
@@ -4709,15 +4709,11 @@
 elds
-.get(1)
+%5B1%5D
 );%0A 

Remove dependency on Guava Splitter and Joiner.