/*
* Copyright 2016 Gleb Godonoga.
*
* 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 com.andrada.sitracker.reader;
import android.net.Uri;
import com.andrada.sitracker.Constants;
import com.andrada.sitracker.contracts.AppUriContract;
import com.andrada.sitracker.db.beans.SearchedAuthor;
import com.andrada.sitracker.exceptions.SearchException;
import com.github.kevinsawicki.http.HttpRequest;
import org.androidannotations.api.BackgroundExecutor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.andrada.sitracker.util.LogUtils.LOGD;
import static com.andrada.sitracker.util.LogUtils.LOGE;
import static com.andrada.sitracker.util.LogUtils.makeLogTag;
public class SamlibSeekSearchStrategyImpl implements SearchStrategy {
private static final String TAG = makeLogTag(SamlibSeekSearchStrategyImpl.class);
private static final String SEARCH_URL = "http://samlib.ru/cgi-bin/seek?DIR=%s&FIND=%s&PLACE=index&JANR=%d&TYPE=%d&PAGE=%d";
private static final String DEFAULT_DIR = "";
private static final String BUFF_READER_ID = "bufferedReader";
private static final int DEFAULT_GENRE = 0;
private static final int DEFAULT_TYPE = 0;
/**
* Use search cache for 1 day only
*/
private static final long MAX_STALE_CACHE = 60 * 60 * 24 * 1;
volatile boolean finishedLoading = false;
private List<SearchedAuthor> mAuthors = new ArrayList<SearchedAuthor>();
private void readData(BufferedReader reader, StringBuffer appendable) throws IOException {
try {
final CharBuffer buffer = CharBuffer.allocate(8192);
int read;
while ((read = reader.read(buffer)) != -1) {
buffer.rewind();
appendable.append(buffer, 0, read);
buffer.rewind();
}
} catch (IOException e) {
LOGE(TAG, "Could not read data", e);
} finally {
try {
reader.close();
} catch (IOException ignored) {
}
finishedLoading = true;
}
}
@Override
public List<SearchedAuthor> searchForQuery(Uri searchUri)
throws MalformedURLException, SearchException, InterruptedException {
String searchString = AppUriContract.getSanitizedSearchQuery(searchUri);
try {
searchString = URLEncoder.encode(searchString, Constants.DEFAULT_SAMLIB_ENCODING);
} catch (UnsupportedEncodingException ignored) {
//Try to just search without encoding the query
}
String url = String.format(SEARCH_URL, DEFAULT_DIR, searchString, DEFAULT_GENRE, DEFAULT_TYPE, 1);
Map<String, SearchedAuthor> hashAuthors = new HashMap<String, SearchedAuthor>();
final long requestStart = new Date().getTime();
final HttpRequest request = HttpRequest.get(new URL(url));
//Tolerate 1 day
request.getConnection().addRequestProperty("Cache-Control", "max-stale=" + MAX_STALE_CACHE);
if (request.code() == 404) {
throw new MalformedURLException();
}
if (request.code() == 500) {
throw new SearchException(SearchException.SearchErrors.SAMLIB_BUSY);
}
final StringBuffer buffer = new StringBuffer();
final BufferedReader reader = request.bufferedReader();
finishedLoading = false;
LOGD(TAG, "Starting search: " + requestStart);
BackgroundExecutor.execute(new BackgroundExecutor.Task(BUFF_READER_ID, 0, "") {
@Override
public void execute() {
try {
readData(reader, buffer);
} catch (Throwable e) {
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
});
Thread.sleep(500);
long currentMils;
boolean authNumberCriteriaSatisfied;
boolean timeCriteriaSatisfied;
do {
currentMils = new Date().getTime();
Collection<SearchedAuthor> authors = new SamlibAuthorSearchReader().getUniqueAuthorsFromPage(buffer.toString());
for (SearchedAuthor auth : authors) {
if (!hashAuthors.containsKey(auth.getAuthorUrl())) {
hashAuthors.put(auth.getAuthorUrl(), auth);
}
}
LOGD(TAG, "Check for result availability. Mils passed: " + (currentMils - requestStart) + ". Unique authors got: " + hashAuthors.size());
authNumberCriteriaSatisfied = hashAuthors.size() > 10;
timeCriteriaSatisfied = (currentMils - requestStart) > 30000 && hashAuthors.size() != 0;
Thread.sleep(500);
} while (!finishedLoading && !authNumberCriteriaSatisfied && !timeCriteriaSatisfied);
if (!finishedLoading) {
LOGD(TAG, "Search conditions satisfied. Force stopping current request with " + hashAuthors.size() + " authors");
finishedLoading = true;
BackgroundExecutor.cancelAll(BUFF_READER_ID, true);
}
mAuthors.addAll(hashAuthors.values());
final String unencodedQuery = AppUriContract.getSanitizedSearchQuery(searchUri).toLowerCase();
Collections.sort(mAuthors, new Comparator<SearchedAuthor>() {
@Override
public int compare(SearchedAuthor searchedAuthor, SearchedAuthor searchedAuthor2) {
return searchedAuthor.weightedCompare(searchedAuthor2, unencodedQuery);
}
});
return mAuthors;
}
@Override
public void cancelAnyRunningTasks() {
finishedLoading = true;
BackgroundExecutor.cancelAll(BUFF_READER_ID, true);
}
}